laravel: up and running: a framework for building modern php

795

Upload: dinhduong

Post on 13-Feb-2017

287 views

Category:

Documents


6 download

TRANSCRIPT

Page 1: Laravel: Up and Running: A Framework for Building Modern PHP
Page 2: Laravel: Up and Running: A Framework for Building Modern PHP
Page 3: Laravel: Up and Running: A Framework for Building Modern PHP

Laravel:UpandRunningAFrameworkforBuildingModernPHPApps

MattStauffer

Page 4: Laravel: Up and Running: A Framework for Building Modern PHP

Laravel:UpandRunningbyMattStauffer

Copyright©2017MattStauffer.Allrightsreserved.

PrintedintheUnitedStatesofAmerica.

PublishedbyO’ReillyMedia,Inc.,1005GravensteinHighwayNorth,Sebastopol,CA95472.

O’Reillybooksmaybepurchasedforeducational,business,orsalespromotionaluse.Onlineeditionsarealsoavailableformosttitles(http://oreilly.com/safari).Formoreinformation,contactourcorporate/institutionalsalesdepartment:[email protected].

Editor:AllysonMacDonald

ProductionEditor:ColleenLobner

Copyeditor:RachelHead

Proofreader:KimCofer

Indexer:AngelaHoward

InteriorDesigner:DavidFutato

CoverDesigner:RandyComer

Illustrator:RebeccaDemarest

December2016:FirstEdition

Page 5: Laravel: Up and Running: A Framework for Building Modern PHP

RevisionHistoryfortheFirstEdition2016-11-14:FirstRelease

Seehttp://oreilly.com/catalog/errata.csp?isbn=9781491936085forreleasedetails.

TheO’ReillylogoisaregisteredtrademarkofO’ReillyMedia,Inc.Laravel:UpandRunning,thecoverimageofagemsbok,andrelatedtradedressaretrademarksofO’ReillyMedia,Inc.

Whilethepublisherandtheauthorhaveusedgoodfaitheffortstoensurethattheinformationandinstructionscontainedinthisworkareaccurate,thepublisherandtheauthordisclaimallresponsibilityforerrorsoromissions,includingwithoutlimitationresponsibilityfordamagesresultingfromtheuseoforrelianceonthiswork.Useoftheinformationandinstructionscontainedinthisworkisatyourownrisk.Ifanycodesamplesorothertechnologythisworkcontainsordescribesissubjecttoopensourcelicensesortheintellectualpropertyrightsofothers,itisyourresponsibilitytoensurethatyourusethereofcomplieswithsuchlicensesand/orrights.

978-1-491-93608-5

[LSI]

Page 6: Laravel: Up and Running: A Framework for Building Modern PHP

DedicationThisbookisdedicatedtomygraciousandinspiringwife,Tereva,myjoyfulandcourageousson,Malachi,andmybeautifuldaughter,Mia,whospentthemajorityofthisbook’screationinhermama’sbelly.

Page 7: Laravel: Up and Running: A Framework for Building Modern PHP

Preface

ThestoryofhowIgotstartedwithLaravelisacommonone:IhadwrittenPHPforyears,butIwasonmywayoutthedoor,pursuingthepowerofRailsandothermodernwebframeworks.Railsinparticularhadalivelycommunity,aperfectcombinationofopinionateddefaultsandflexibility,andthepowerofRubyGemstoleverageprepackagedcommoncode.

Somethingkeptmefromjumpingship,andIwasgladforthatwhenIfoundLaravel.ItofferedeverythingIwasdrawntoinRails,butitwasn’tjustaRailsclone;thiswasaninnovativeframeworkwithincredibledocumentation,awelcomingcommunity,andclearinfluencesfrommanylanguagesandframeworks.

SincethatdayI’vebeenabletosharemyjourneyoflearningLaravelthroughbloggingandspeakingatconferences;I’vewrittendozensofappsinLaravelforsideandworkprojects,andI’vemetthousandsofLaraveldevelopersonlineandinperson.Ihaveplentyoftoolsinmytoolkitatourconsultancy,butIamhonestlyhappiestwhenIsitdowninfrontofacommandlineandtypelaravelnewproject.

Page 8: Laravel: Up and Running: A Framework for Building Modern PHP

WhatThisBookIsAboutThisisnotthefirstbookaboutLaravel,anditwon’tbethelast.Idon’tintendforthistobeabookthatcoverseverylineofcodeoreveryimplementationpattern.Idon’twantthistobethesortofbookthatgoesoutofdatewhenanewversionofLaravelisreleased.Instead,itsprimarypurposeistoprovidedeveloperswithahigh-leveloverviewandconcreteexamplestolearnwhattheyneedtogetstarted,asquicklyaspossible.Ratherthanmirroringthedocs,IwanttohelpyouunderstandthefoundationalconceptsbehindLaravel.

LaravelisapowerfulandflexiblePHPframework.Ithasathrivingcommunityandawideecosystemoftools,andasaresultit’sgrowinginappealandreach.ThisbookisfordeveloperswhoalreadyknowhowtomakewebsitesandapplicationsandwanttoquicklylearnhowtodosoinLaravel.

Laravel’sdocumentationisthoroughandexcellent.IfyoufindthatIdon’tcoveranyparticulartopicdeeplyenoughforyourliking,Iencourageyoutovisittheonlinedocumentationanddigdeeperintothatparticulartopic.

Ithinkyouwillfindthebookacomfortablebalancebetweenhigh-levelintroductionandconcreteapplication,andbytheendyoushouldfeelcomfortablewritinganentireapplicationinLaravel,fromscratch.And,ifIdidmyjobwell,you’llbeexcitedtotry.

Page 9: Laravel: Up and Running: A Framework for Building Modern PHP

WhoThisBookIsForThisbookassumesknowledgeofbasicobject-orientedprogrammingpractices,PHP(oratleastthegeneralsyntaxofC-familylanguages),andthebasicconceptsoftheModel–View–Controller(MVC)patternandtemplating.Ifyou’venevermadeawebsitebefore,youmayfindyourselfinoveryourhead.Butaslongasyouhavesomeprogrammingexperience,youdon’thavetoknowanythingaboutLaravelbeforeyoureadthisbook—we’llcovereverythingyouneedtoknow,fromthesimplest“Hello,world!”

Laravelcanrunonanyoperatingsystem,buttherewillbesomeBash(shell)commandsinthebookthatareeasiesttorunonLinux/MacOS.WindowsusersmayhaveahardertimewiththesecommandsandwithmodernPHPdevelopment,butifyoufollowtheinstructionstogetHomestead(aLinuxvirtualmachine)running,you’llbeabletorunallofthecommandsfromthere.

Page 10: Laravel: Up and Running: A Framework for Building Modern PHP

HowThisBookIsStructuredThisbookisstructuredinwhatIimaginetobeachronologicalorder:ifyou’rebuildingyourfirstwebappwithLaravel,theearlychapterscoverthefoundationalcomponentsyou’llneedtogetstarted,andthelaterchapterscoverlessfoundationalormoreesotericfeatures.

Eachsectionofthebookcanbereadonitsown,butforsomeonenewtotheframework,I’vetriedtostructurethechapterssothatit’sactuallyveryreasonabletostartfromthebeginningandreaduntiltheend.

Whereapplicable,eachchapterwillendwithtwosections:“Testing”and“TL;DR.”Ifyou’renotfamiliar,TL;DRmeans“toolong;didn’tread.”Thesefinalsectionswillshowyouhowtowritetestsforthefeaturescoveredineachchapterandgiveahigh-leveloverviewofwhatwascovered.

ThebookiswrittenforLaravel5.3,butbecauseLaravel5.1isthelatestLTSrelease,anyfeaturesthatarenewin5.2or5.3willbeidentified.

Page 11: Laravel: Up and Running: A Framework for Building Modern PHP

ConventionsUsedinThisBookThefollowingtypographicalconventionsareusedinthisbook:

ItalicIndicatesnewterms,URLs,emailaddresses,filenames,andfileextensions.

Constantwidth

Usedforprogramlistings,aswellaswithinparagraphstorefertoprogramelementssuchasvariableorfunctionnames,databases,datatypes,environmentvariables,statements,andkeywords.

Constantwidthbold

Showscommandsorothertextthatshouldbetypedliterallybytheuser.

Constantwidthitalic

Showstextthatshouldbereplacedwithuser-suppliedvaluesorbyvaluesdeterminedbycontext.

TIPThiselementsignifiesatiporsuggestion.

NOTEThiselementsignifiesageneralnote.

WARNINGThiselementindicatesawarningorcaution.

Page 12: Laravel: Up and Running: A Framework for Building Modern PHP

O’ReillySafariNOTE

Safari(formerlySafariBooksOnline)ismembership-basedtrainingandreferenceplatformforenterprise,government,educators,andindividuals.

Membershaveaccesstothousandsofbooks,trainingvideos,LearningPaths,interactivetutorials,andcuratedplaylistsfromover250publishers,includingO’ReillyMedia,HarvardBusinessReview,PrenticeHallProfessional,Addison-WesleyProfessional,MicrosoftPress,Sams,Que,PeachpitPress,Adobe,FocalPress,CiscoPress,JohnWiley&Sons,Syngress,MorganKaufmann,IBMRedbooks,Packt,AdobePress,FTPress,Apress,Manning,NewRiders,McGraw-Hill,Jones&Bartlett,andCourseTechnology,amongothers.

Formoreinformation,pleasevisithttp://oreilly.com/safari.

Page 13: Laravel: Up and Running: A Framework for Building Modern PHP

HowtoContactUsPleaseaddresscommentsandquestionsconcerningthisbooktothepublisher:

O’ReillyMedia,Inc.

1005GravensteinHighwayNorth

Sebastopol,CA95472

800-998-9938(intheUnitedStatesorCanada)

707-829-0515(internationalorlocal)

707-829-0104(fax)

Wehaveawebpageforthisbook,wherewelisterrata,examples,andanyadditionalinformation.Youcanaccessthispageathttp://bit.ly/laravel-up-and-running.

Tocommentorasktechnicalquestionsaboutthisbook,[email protected].

Formoreinformationaboutourbooks,courses,conferences,andnews,seeourwebsiteathttp://www.oreilly.com.

FindusonFacebook:http://facebook.com/oreilly

FollowusonTwitter:http://twitter.com/oreillymedia

WatchusonYouTube:http://www.youtube.com/oreillymedia

Page 14: Laravel: Up and Running: A Framework for Building Modern PHP

AcknowledgmentsThisbookwouldnothavehappenedwithoutthegracioussupportofmyamazingwifeTerevaortheunderstanding(“Daddy’swriting,buddy!”)ofmysonMalachi.Andwhileshewasn’texplicitlyawareofit,mydaughterMiawasaroundforalmosttheentirecreationofthebook,sothisbookisdedicatedtothewholefamily.Thereweremany,manylongeveninghoursandweekendStarbuckstripsthattookmeawayfrommyfamily,andIcouldn’tbemoregratefulfortheirsupportandalsotheirpresencejustmakingmylifeawesome.

Additionally,theentireTightenCo.familyhassupportedandencouragedmethroughthewritingofthebook,severalevenediting(KeithDamiani,editorextraordinaire)andhelpingmewithchallengingcodesamples(AdamWathan,KingoftheCollectionPipeline).DanSheetz,mypartnerinTightencrime,hasbeengraciousenoughtowatchmewhileawaymanyaworkhourcrankingonthisbookandwasnothingbutsupportiveandencouraging;andDaveHicking,ouroperationsmanager,helpedmearrangemyscheduleandworkresponsibilitiesaroundwritingtime.

TaylorOtwelldeservesthanksandhonorforcreatingLaravel—andthereforecreatingsomanyjobsandhelpingsomanydevelopersloveourlivesthatmuchmore.Hedeservesappreciationforhowhe’sfocusedondeveloperhappinessandhowhardhe’sworkedtohaveempathyfordevelopersandtobuildapositiveandencouragingcommunity.ButIalsowanttothankhimforbeingakind,encouraging,andchallengingfriend.Taylor,you’reaboss.

ThankstoJeffreyWay,whoIstillcontendtobeoneofthebestteachersontheInternet.HeoriginallyintroducedmetoLaravelandstillintroducesmorepeopleeveryday.He’salso,unsurprisingly,afantastichumanbeingwhomI’mgladtocallafriend.

ThankyoutoJessD’Amico,ShawnMcCool,IanLandsman,andTaylorforseeingvalueinmeasaconferencespeakerearlyonandgivingmeaplatformtoteachfrom.ThankstoDayleReesformakingitsoeasyforsomanytolearnLaravelintheearlydays.

ThankstoeverypersonwhoputtheirtimeandeffortintowritingblogpostsaboutLaravel,especiallyearlyon:EricBarnes,ChrisFidao,MattMachuga,JasonLewis,RyanTablada,DriesVints,MaksSurguy,andsomanymore.

AndthankstotheentirecommunityoffriendsonTwitter,IRC,andSlackwho’veinteractedwithmeovertheyears.IwishIcouldnameeveryname,butIwouldmisssomeandthenfeelawfulaboutmissingthem.Youallarebrilliant,andI’mhonoredtogettointeractwithyouonaregularbasis.

ThankstomyO’Reillyeditor,AllyMacDonald,andallofmytechnicaleditors:KeithDamiani,MichaelDyrynda,AdamFairholm,andMylesHyson.

And,ofcourse,thankstotherestofmyfamilyandfriends,whosupportedmedirectlyorindirectlythroughthisprocess—myparentsandsiblings,theGainesvillecommunity,otherbusinessownersandauthors,otherconferencespeakers,andtheinimitableDCB.Ineedto

Page 15: Laravel: Up and Running: A Framework for Building Modern PHP

stopwritingbecausebythetimeIrunoutofspacehereI’llbethankingmyStarbucksbaristas.

Page 16: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter1.WhyLaravel?

Intheearlydaysofthedynamicweb,writingawebapplicationlookedalotdifferentthanitdoestoday.Developersthenwereresponsibleforwritingthecodefornotjusttheuniquebusinesslogicofourapplications,butalsoeachofthecomponentsthataresocommonacrosssites—userauthentication,inputvalidation,databaseaccess,templating,andmore.

Today,programmershavedozensofapplicationdevelopmentframeworksandthousandsofcomponentsandlibrarieseasilyaccessible.It’sacommonrefrainamongprogrammersthat,bythetimeyoulearnoneframework,threenewer(andpurportedlybetter)frameworkshavepoppedupintendingtoreplaceit.

“Justbecauseit’sthere”mightbeavalidjustificationforclimbingamountain,buttherearebetterreasonstochoosetouseaspecificframework—ortouseaframeworkatall.It’sworthaskingthequestion:whyframeworks?Morespecifically,whyLaravel?

Page 17: Laravel: Up and Running: A Framework for Building Modern PHP

WhyUseaFramework?It’seasytoseewhyit’sbeneficialtousetheindividualcomponents,orpackages,thatareavailabletoPHPdevelopers.Withpackages,someoneelseisresponsiblefordevelopingandmaintaininganisolatedpieceofcodethathasawell-definedjob,andintheorythatpersonhasadeeperunderstandingofthissinglecomponentthanyouhavetimetohave.

FrameworkslikeLaravel—andSymfony,Silex,Lumen,andSlim—prepackageacollectionofthird-partycomponentstogetherwithcustomframework“glue”likeconfigurationfiles,serviceproviders,prescribeddirectorystructures,andapplicationbootstraps.So,thebenefitofusingaframeworkingeneralisthatsomeonehasmadedecisionsnotjustaboutindividualcomponentsforyou,butalsoabouthowthosecomponentsshouldfittogether.

Page 18: Laravel: Up and Running: A Framework for Building Modern PHP

“I’llJustBuildItMyself”Let’ssayyoustartanewwebappwithoutthebenefitofaframework.Wheredoyoubegin?Well,itshouldprobablyrouteHTTPrequests,soyounowneedtoevaluatealloftheHTTPrequestandresponselibrariesavailableandpickone.Thenarouter.Oh,andyou’llprobablyneedtosetupsomeformofroutesconfigurationfile.Whatsyntaxshouldituse?Whereshoulditgo?Whataboutcontrollers?Wheredotheylive,andhowaretheyloaded?Well,youprobablyneedadependencyinjectioncontainertoresolvethecontrollersandtheirdependencies.Butwhichone?

Furthermore,whatifyoudotakethetimetoanswerallthosequestionsandsuccessfullycreateyourapplication—what’stheimpactonthenextdeveloper?Whataboutwhenyouhavefoursuchcustom-framework–basedapplications,oradozen,andyouhavetorememberwherethecontrollersliveineach,orwhattheroutingsyntaxis?

Page 19: Laravel: Up and Running: A Framework for Building Modern PHP

ConsistencyandFlexibilityFrameworksaddressthisissuebyprovidingacarefullyconsideredanswertothequestion“Whichcomponentshouldweusehere?”andensuringthattheparticularcomponentschosenworkwelltogether.Additionally,frameworksprovideconventionsthatreducetheamountofcodeadevelopernewtotheprojecthastounderstand—ifyouunderstandhowroutingworksinoneLaravelproject,forexample,youunderstandhowitworksinallLaravelprojects.

Whensomeoneprescribesrollingyourownframeworkforeachnewproject,whatthey’rereallyadvocatingistheabilitytocontrolwhatdoesanddoesn’tgointoyourapplication’sfoundation.Thatmeansthebestframeworkswillnotonlyprovideyouwithasolidfoundation,butalsogiveyouthefreedomtocustomizetoyourheart’scontent.Andthis,asI’llshowyouintherestofthisbook,ispartofwhatmakesLaravelsospecial.

Page 20: Laravel: Up and Running: A Framework for Building Modern PHP

AShortHistoryofWebandPHPFrameworksAnimportantpartofbeingabletoanswerthequestion“WhyLaravel?”isunderstandingLaravel’shistory—andunderstandingwhatcamebeforeit.PriortoLaravel’sriseinpopularity,therewereavarietyofframeworksandothermovementsinPHPandotherwebdevelopmentspaces.

Page 21: Laravel: Up and Running: A Framework for Building Modern PHP

RubyonRailsDavidHeinemeierHanssonreleasedthefirstversionofRubyonRailsin2004,andit’sbeenhardtofindawebapplicationframeworksincethenthathasn’tbeeninfluencedbyRailsinsomeway.

RailspopularizedMVC,RESTfulJSONAPIs,conventionoverconfiguration,ActiveRecord,andmanymoretoolsandconventionsthathadaprofoundinfluenceonthewaywebdevelopersapproachedtheirapplications—especiallywithregardtorapidapplicationdevelopment.

Page 22: Laravel: Up and Running: A Framework for Building Modern PHP

TheInfluxofPHPFrameworksItwascleartomostdevelopersthatRails,andsimilarwebapplicationframeworks,werethewaveofthefuture,andPHPframeworks,includingthoseadmittedlyimitatingRails,startingpoppingupquickly.

CakePHPwasthefirstin2005,anditwassoonfollowedbySymfony,CodeIgniter,ZendFramework,andKohana(aCodeIgniterfork).Yiiarrivedin2008,andAuraandSlimin2010.2011broughtFuelPHPandLaravel,bothofwhichwerenotquiteCodeIgniteroffshoots,butinsteadproposedasalternatives.

SomeoftheseframeworksweremoreRails-y,focusingondatabaseobject-relationalmappers(ORMs),MVCstructures,andothertoolstargetingrapiddevelopment.Others,likeSymfonyandZend,focusedmoreonenterprisedesignpatternsandecommerce.

Page 23: Laravel: Up and Running: A Framework for Building Modern PHP

TheGoodandtheBadofCodeIgniterCakePHPandCodeIgniterwerethetwoearlyPHPframeworksthatweremostopenabouthowmuchtheirinspirationwasdrawnfromRails.CodeIgniterquicklyrosetofameandby2010wasarguablythemostpopularoftheindependentPHPframeworks.

CodeIgniterwassimple,easytouse,andboastedamazingdocumentationandastrongcommunity.Butitsuseofmoderntechnologyandpatternsadvancedslowly,andastheframeworkworldgrewandPHP’stoolingadvanced,CodeIgniterstartedfallingbehindintermsofbothtechnologicaladvancesandout-of-the-boxfeatures.Unlikemanyotherframeworks,CodeIgniterwasmanagedbyacompany,andtheywereslowtocatchupwithPHP5.3’snewerfeatureslikenamespacesandthemovestoGitHubandlaterComposer.Itwasin2010thatTaylorOtwell,Laravel’screator,becamedissatisfiedenoughwithCodeIgniterthathesetofftowritehisownframework.

Page 24: Laravel: Up and Running: A Framework for Building Modern PHP

Laravel1,2,and3ThefirstbetaofLaravel1wasreleasedinJune2011,anditwaswrittencompletelyfromscratch.ItfeaturedacustomORM(Eloquent);closure-basedrouting(inspiredbyRubySinatra);amodulesystemforextension;andhelpersforforms,validation,authentication,andmore.

EarlyLaraveldevelopmentmovedquickly,andLaravel2and3werereleasedinNovember2011andFebruary2012,respectively.Theyintroducedcontrollers,unittesting,acommand-linetool,aninversionofcontrol(IoC)container,Eloquentrelationships,andmigrations.

Page 25: Laravel: Up and Running: A Framework for Building Modern PHP

Laravel4WithLaravel4,Taylorrewrotetheentireframeworkfromthegroundup.BythispointComposer,PHP’snow-ubiquitouspackagemanager,wasshowingsignsofbecominganindustrystandardandTaylorsawthevalueofrewritingtheframeworkasacollectionofcomponents,distributedandbundledtogetherbyComposer.

TaylordevelopedasetofcomponentsunderthecodenameIlluminateand,inMay2013,releasedLaravel4withanentirelynewstructure.Insteadofbundlingthemajorityofitscodeasadownload,LaravelnowpulledinthemajorityofitscomponentsfromSymfony(anotherframeworkthatreleaseditscomponentsforusebyothers)andtheIlluminatecomponentsthroughComposer.

Laravel4alsointroducedqueues,amailcomponent,facades,anddatabaseseeding.AndbecauseLaravelwasnowrelyingonSymfonycomponents,itwasannouncedthatLaravelwouldbemirroring(notexactly,butsoonafter)thesix-monthlyreleasescheduleSymfonyfollows.

Page 26: Laravel: Up and Running: A Framework for Building Modern PHP

Laravel5Laravel4.3wasscheduledtoreleaseinNovember2014,butasdevelopmentprogressed,itbecameclearthatthesignificanceofitschangesmeritedamajorrelease,andLaravel5wasreleasedinFebruary2015.

Laravel5featuredarevampeddirectorystructure,removaloftheformandHTMLhelpers,theintroductionofthecontractinterfaces,aspateofnewviews,Socialiteforsocialmediaauthentication,Elixirforassetcompilation,Schedulertosimplifycron,dotenvforsimplifiedenvironmentmanagement,formrequests,andabrandnewREPL(read–evaluate–printloop).

Page 27: Laravel: Up and Running: A Framework for Building Modern PHP

What’sSoSpecialAboutLaravel?SowhatisitthatsetsLaravelapart?WhyisitworthhavingmorethanonePHPframeworkatanytime?TheyallusecomponentsfromSymfonyanyway,right?Let’stalkabitaboutwhatmakesLaravel“tick.”

Page 28: Laravel: Up and Running: A Framework for Building Modern PHP

ThePhilosophyofLaravelYouonlyneedtoreadthroughtheLaravelmarketingmaterialsandREADMEstostartseeingitsvalues.Tayloruseslight-relatedwordslike“Illuminate”and“Spark.”Andthentherearethese:“Artisans.”“Elegant.”Also,these:“Breathoffreshair.”“Freshstart.”Andfinally:“Rapid.”“Warpspeed.”

Thetwomoststronglycommunicatedvaluesoftheframeworkaretoincreasedeveloperspeedanddeveloperhappiness.Taylorhasdescribedthe“Artisan”languageasintentionallycontrastingagainstmoreutilitarianvalues.Youcanseethegenesisofthissortofthinkinginhis2011questiononStackExchangeinwhichhestated,“SometimesIspendridiculousamountsoftime(hours)agonizingovermakingcodelookpretty”—justforthesakeofabetterexperienceoflookingatthecodeitself.Andhe’softentalkedaboutthevalueofmakingiteasierandquickerfordeveloperstotaketheirideastofruition,gettingridofunnecessarybarrierstocreatinggreatproducts.

Laravelis,atitscore,aboutequippingandenablingdevelopers.Itsgoalistoprovideclear,simple,andbeautifulcodeandfeaturesthathelpdevelopersquicklylearn,start,anddevelop,andwritecodethat’ssimple,clear,andwilllast.

TheconceptoftargetingdevelopersisclearacrossLaravelmaterials.“Happydevelopersmakethebestcode”iswritteninthedocumentation.“Developerhappinessfromdownloadtodeploy”wastheunofficialsloganforawhile.Ofcourse,anytoolorframeworkwillsayitwantsdeveloperstobehappy.Buthavingdeveloperhappinessasaprimaryconcern,ratherthansecondary,hashadahugeimpactonLaravel’sstyleanddecision-makingprogress.Whereotherframeworksmaytargetarchitecturalpurityastheirprimarygoal,orcompatibilitywiththegoalsandvaluesofenterprisedevelopmentteams,Laravel’sprimaryfocusisonservingtheindividualdeveloper.

Page 29: Laravel: Up and Running: A Framework for Building Modern PHP

HowLaravelAchievesDeveloperHappinessJustsayingyouwanttomakedevelopershappyisonething.Doingitisanother,anditrequiresyoutoquestionwhatinaframeworkismostlikelytomakedevelopersunhappyandwhatismostlikelytomakethemhappy.ThereareafewwaysLaraveltriestomakedevelopers’liveseasier.

First,Laravelisarapidapplicationdevelopmentframework.Thatmeansitfocusesonashallow(easy)learningcurveandonminimizingthestepsbetweenstartinganewappandpublishingit.Allofthemostcommontasksinbuildingwebapplications,fromdatabaseinteractionstoauthenticationtoqueuestoemailtocaching,aremadesimplerbythecomponentsLaravelprovides.ButLaravel’scomponentsaren’tjustgreatontheirown;theyprovideaconsistentAPIandpredictablestructuresacrosstheentireframework.Thatmeansthat,whenyou’retryingsomethingnewinLaravel,you’remorethanlikelygoingtoendupsaying,“…anditjustworks.”

Thisdoesn’tendattheframeworkitself,either.Laravelprovidesanentireecosystemoftoolsforbuildingandlaunchingapplications.YouhaveHomesteadandValetforlocaldevelopment,Forgeforservermanagement,andEnvoyerforadvanceddeployment.Andthere’sasuiteofadd-onpackages:Cashierforpaymentsandsubscriptions,EchoforWebSockets,Scoutforsearch,PassportforAPIauthentication,Socialiteforsociallogin,andSparktobootstrapyourSaaS.Laravelistryingtotaketherepetitiveworkoutofdevelopers’jobssotheycandosomethingunique.

Next,Laravelfocuseson“conventionoverconfiguration”—meaningthatifyou’rewillingtouseLaravel’sdefaults,you’llhavetodomuchlessworkthanwithotherframeworksthatrequireyoutodeclareallofyoursettingsevenifyou’reusingtherecommendedconfiguration.ProjectsbuiltonLaraveltakelesstimethanthosebuiltonmostotherPHPframeworks.

Laravelalsofocusesdeeplyonsimplicity.It’spossibletousedependencyinjectionandmockingandtheDataMapperpatternandrepositoriesandCommandQueryResponsibilitySegregationandallsortsofothermorecomplexarchitecturalpatternswithLaravel,ifyouwant.Butwhileotherframeworksmightsuggestusingthosetoolsandstructuresoneveryproject,Laravelanditsdocumentationandcommunityleantowardstartingwiththesimplestpossibleimplementation—aglobalfunctionhere,afacadethere,ActiveRecordoverthere.Thisallowsdeveloperstocreatethesimplestpossibleapplicationtosolvefortheirneeds.

AninterestingsourceofhowLaravelisdifferentisthatitscreatoranditscommunityaremoreconnectedtoandinspiredbyRubyandRailsandfunctionalprogramminglanguagesthanbyJava.There’sastrongcurrentinmodernPHPtoleantowardverbosityandcomplexity,embracingthemoreJava-esqueaspectsofPHP.ButLaraveltendstobeontheotherside,embracingexpressive,dynamic,andsimplecodingpracticesandlanguagefeatures.

Page 30: Laravel: Up and Running: A Framework for Building Modern PHP

TheLaravelCommunityIfthisbookisyourfirstexposuretotheLaravelcommunity,youhavesomethingspecialtolookforwardto.OneofthedistinguishingelementsofLaravel,whichhascontributedtoitsgrowthandsuccess,isthewelcoming,teachingcommunitythatsurroundsit.FromJeffreyWay’sLaracastsvideotutorialstoLaravelNewstoSlackandIRCchannels,fromTwitterfriendstobloggerstotheLaraconconferences,Laravelhasarichandvibrantcommunityfulloffolkswho’vebeenaroundsincedayoneandfolkswhoareontheirowndayone.Andthisisn’tanaccident:

FromtheverybeginningofLaravel,I’vehadthisideathatallpeoplewanttofeelliketheyarepartofsomething.It’sanaturalhumaninstincttowanttobelongandbeacceptedintoagroupofotherlike-mindedpeople.So,byinjectingpersonalityintoawebframeworkandbeingreallyactivewiththecommunity,thattypeoffeelingcangrowinthecommunity.TaylorOtwell,ProductandSupportinterview

TaylorunderstoodfromtheearlydaysofLaravelthatasuccessfulopensourceprojectneededtwothings:gooddocumentationandawelcomingcommunity.AndthosetwothingsarenowhallmarksofLaravel.

Page 31: Laravel: Up and Running: A Framework for Building Modern PHP

HowItWorksUpuntilnow,everythingI’vesharedherehasbeenentirelyabstract.Whataboutthecode,youask?Let’sdigintoasimpleapplication(Example1-1)soyoucanseewhatworkingwithLaravelday-to-dayisactuallylike.

Example1-1.“Hello,World”inroutes/web.php//File:routes/web.php

<?php

Route::get('/',function(){

return'Hello,World!';

});

ThesimplestpossibleactionyoucantakeinaLaravelapplicationistodefinearouteandreturnaresultanytimesomeonevisitsthatroute.IfyouinitializeabrandnewLaravelapplicationonyourmachine,definetherouteinExample1-1,andthenservethesitefromthepublicdirectory,you’llhaveafullyfunctioning“Hello,World”example(seeFigure1-1).

Figure1-1.Returning“Hello,World!”withLaravel

Itlooksverysimilartodothesamewithcontrollers,asyoucanseeinExample1-2.

Example1-2.“Hello,World”withcontrollers//File:routes/web.php

<?php

Route::get('/','WelcomeController@index');

//File:app/Http/Controllers/WelcomeController.php

<?php

namespaceapp\Http\Controllers;

classWelcomeController

{

publicfunctionindex()

{

return'Hello,World!';

}

}

Andifwe’restoringourgreetingsinthedatabase,it’llalsolookprettysimilar(seeExample1-3).

Example1-3.Multigreeting“Hello,World”withdatabaseaccess//File:routes/web.php

<?php

Page 32: Laravel: Up and Running: A Framework for Building Modern PHP

Route::get('/',function(){

returnGreeting::first()->body;

});

//File:app/Greeting.php

<?php

useIlluminate\Database\Eloquent\Model;

classGreetingextendsModel{}

//File:database/migrations/2015_07_19_010000_create_greetings_table.php

<?php

useIlluminate\Database\Migrations\Migration;

useIlluminate\Database\Schema\Blueprint;

classCreateGreetingsTableextendsMigration

{

publicfunctionup()

{

Schema::create('greetings',function(Blueprint$table){

$table->increments('id');

$table->string('body');

$table->timestamps();

});

}

publicfunctiondown()

{

Schema::drop('greetings');

}

}

Example1-3mightbeabitoverwhelming,andifso,justskipoverit.We’lllearnabouteverythingthat’shappeninghereinlaterchapters,butyoucanalreadyseethatwithjustafewlinesofcode,we’vesetupdatabasemigrationsandmodelsandpulledrecordsout.It’sjustthatsimple.

Page 33: Laravel: Up and Running: A Framework for Building Modern PHP

WhyLaravel?So—whyLaravel?

BecauseLaravelhelpsyoubringyourideastorealitywithnowastedcode,usingmoderncodingstandards,surroundedbyavibrantcommunity,withanempoweringecosystemoftools.

Andbecauseyou,deardeveloper,deservetobehappy.

Page 34: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter2.SettingUpaLaravelDevelopmentEnvironment

PartofPHP’ssuccesshasbeenbecauseit’shardtofindawebserverthatcan’tservePHP.However,modernPHPtoolshavestricterrequirementsthanthoseofthepast.ThebestwaytodevelopforLaravelistoensureaconsistentlocalandremoteserverenvironmentforyourcode,andthankfully,theLaravelecosystemhasafewtoolsforthis.

Page 35: Laravel: Up and Running: A Framework for Building Modern PHP

SystemRequirementsEverythingwe’llcoverinthischapterispossiblewithWindowsmachines,butyou’llneeddozensofpagesofcustominstructionsandcaveats.I’llleavethoseinstructionsandcaveatstoactualWindowsusers,sotheexampleshereandintherestofthebookwillfocusonUnix/Linux/MacOSdevelopers.

WhetheryouchoosetoserveyourwebsitebyinstallingPHPandothertoolsonyourlocalmachine,serveyourdevelopmentenvironmentfromavirtualmachineviaVagrant,orrelyonatoollikeMAMP/WAMP/XAMPP,yourdevelopmentenvironmentwillneedtohaveallofthefollowinginstalledinordertoserveLaravelsites:

PHP>=5.6.4forLaravel5.3orPHP>=5.5.9for5.1and5.2

OpenSSLPHPextension

PDOPHPextension

MbstringPHPextension

TokenizerPHPextension

Page 36: Laravel: Up and Running: A Framework for Building Modern PHP

ComposerWhatevermachineyou’redevelopingonwillneedtohaveComposerinstalledglobally.Ifyou’renotfamiliarwithComposer,it’satoolthat’satthefoundationofmostmodernPHPdevelopment.ComposerisadependencymanagerforPHP,muchlikeNPMforNodeorRubyGemsforRuby.You’llneedComposertoinstallLaravel,updateLaravel,andbringinexternaldependencies.

Page 37: Laravel: Up and Running: A Framework for Building Modern PHP

LocalDevelopmentEnvironmentsFormanyprojects,hostingyourdevelopmentenvironmentusingasimplertoolsetwillbeenough.IfyoualreadyhaveMAMPorWAMPorXAMPPinstalledonyoursystem,thatwilllikelybefinetorunLaravel.YoucanalsojustrunLaravelwithPHP’sbuilt-inwebserver,assumingyoursystemPHPistherightversion.

AllyoureallyneedtogetstartedistheabilitytorunPHP.Everythingpastthatisuptoyou.

However,Laravelofferstwotoolsforlocaldevelopment,ValetandHomestead,andwe’llcoverbothbriefly.Ifyou’reunsureofwhichtouse,I’drecommendusingValetandjustskimmingtheHomesteadsection;however,bothtoolsarevaluableandworthunderstanding.

Page 38: Laravel: Up and Running: A Framework for Building Modern PHP

LaravelValetIfyouwanttousePHP’sbuilt-inwebserver,yoursimplestoptionistoserveeverysitefromalocalhostURL.Ifyourunphp-Slocalhost:8000-tpublicfromyourLaravelsite’srootfolder,PHP’sbuilt-inwebserverwillserveyoursiteathttp://localhost:8000/.Youcanalsorunphpartisanserveonceyouhaveyourapplicationsetuptoeasilyspinupanequivalentserver.

Butifyou’reinterestedintyingeachofyoursitestoaspecificdevelopmentdomain,you’llneedtogetcomfortablewithyouroperatingsystem’shostsfileanduseatoollikednsmasq.Let’sinsteadtrysomethingsimpler.

Ifyou’reaMacuser(therearealsounofficialforksforWindowsandLinux),LaravelValettakesawaytheneedtoconnectyourdomainstoyourapplicationfolders.ValetinstallsdnsmasqandaseriesofPHPscriptsthatmakeitpossibletotypelaravelnewmyapp&&openmyapp.devandforittojustwork.You’llneedtoinstallafewtoolsusingHomebrew,whichthedocumentationwillwalkyouthrough,butthestepsfrominitialinstallationtoservingyourappsarefewandsimple.

InstallValet(seethedocsforthelatestinstallationinstruction—it’sunderveryactivedevelopmentatthistimeofwriting),andpointitatoneormoredirectorieswhereyoursiteswilllive.Iranvaletparkfrommy~/Sitesdirectory,whichiswhereIputallofmyunder-developmentapps.Now,youcanjustadd.devtotheendofthedirectorynameandvisititinyourbrowser.

Valetmakesiteasytoserveallfoldersinagivenfolderas“FOLDERNAME.dev”usingvaletpark,toservejustasinglefolderusingvaletlink,toopentheValet-serveddomainforafolderusingvaletopen,toservetheValetsitewithHTTPSusingvaletsecure,andtoopenanngroktunnelsoyoucanshareyoursitewithotherswithvaletshare.

Page 39: Laravel: Up and Running: A Framework for Building Modern PHP

LaravelHomesteadHomesteadisanothertoolyoumightwanttousetosetupyourlocaldevelopmentenvironment.It’saconfigurationtoolthatsitsontopofVagrantandprovidesapre-configuredvirtualmachineimagethatisperfectlysetupforLaraveldevelopment,andmirrorsthemostcommonproductionenvironmentthatmanyLaravelsitesrunon.

SettingupHomesteadIfyouchoosetouseHomestead,it’sgoingtotakeabitmoreworktosetupthansomethinglikeMAMPorValet.Thebenefitsaremyriad,however:configuredcorrectly,yourlocalenvironmentcanbeincrediblyclosetoyourremoteworkingenvironment;youwon’thavetoworryaboutupdatingyourdependenciesonyourlocalmachine;andyoucanlearnallaboutthestructureofUbuntuserversfromthesafetyofyourlocalmachine.

WHATTOOLSDOHOMESTEADOFFER?

YoucanalwaysupgradetheindividualcomponentsofyourHomesteadvirtualmachine,buthereareafewimportanttoolsHomesteadcomeswithbydefault:

Toruntheserverandservethesite,Ubuntu,PHP,andNginx(awebserversimilartoApache).

Fordatabase/storageandqueues,MySQL,Postgres,Redis,Memcached,andbeanstalkd.

Forbuildstepsandothertools,Node.

InstallingHomestead’sdependenciesFirst,you’llneedtodownloadandinstalleitherVirtualBoxorVMWare.VirtualBoxismostcommonbecauseit’sfree.

Next,downloadandinstallVagrant.

Vagrantisconvenientbecauseitmakesiteasyforyoutocreateanewlocalvirtualmachinefromaprecreated“box,”whichisessentiallyatemplateforavirtualmachine.So,thenextstepistorunvagrantboxaddlaravel/homesteadfromyourterminaltodownloadthebox.

InstallingHomesteadNext,let’sactuallyinstallHomestead.YoucaninstallmultipleinstancesofHomestead(perhapshostingadifferentHomesteadboxperproject),butIpreferasingleHomesteadvirtualmachineforallofmyprojects.Ifyouwantoneperproject,you’llwanttoinstallHomesteadinyourprojectdirectory;checktheHomesteaddocumentationonlineforinstructions.Ifyouwantasinglevirtualmachineforallofyourprojects,installHomesteadinyouruser ’shomedirectoryusingthefollowingcommand:

gitclonehttps://github.com/laravel/homestead.git~/Homestead

Page 40: Laravel: Up and Running: A Framework for Building Modern PHP

Now,runtheinitializationscriptfromwhereveryouputtheHomesteaddirectory:

bash~/Homestead/init.sh

ThiswillplaceHomestead’sprimaryconfigurationfile,Homestead.yaml,inanew~/.homesteaddirectory.

ConfiguringHomesteadOpenupHomestead.yamlandconfigureithowyou’dlike.Here’swhatitlookslikeoutofthebox:

ip:"192.168.10.10"

memory:2048

cpus:1

provider:virtualbox

authorize:~/.ssh/id_rsa.pub

keys:

-~/.ssh/id_rsa

folders:

-map:~/Code

to:/home/vagrant/Code

sites:

-map:homestead.app

to:/home/vagrant/Code/Laravel/public

databases:

-homestead

#blackfire:

#-id:foo

#token:bar

#client-id:foo

#client-token:bar

#ports:

#-send:50000

#to:5000

#-send:7777

#to:777

#protocol:udp

You’llneedtotellityourprovider(likelyvirtualbox),pointittoyourpublicSSHkey(bydefault~/.ssh/id_rsa.pub;ifyoudon’thaveone,GitHubhasagreattutorialoncreatingSSHkeys),mapfoldersandsitestotheirlocalmachineequivalents,andprovisionadatabase.

MappingfoldersinHomesteadallowsyoutoeditfilesonyourlocalmachineandhavethosefilesshowupinyourVagrantboxsotheycanbeserved.Forexample,ifyouhavea~/Sitesdirectorywhereyouputallofyourcode,you’dmapthefoldersinHomesteadbyreplacingthefolderssectioninHomestead.yamlwiththefollowing:

folders:

-map:~/Sites

to:/home/vagrant/Sites

Page 41: Laravel: Up and Running: A Framework for Building Modern PHP

You’venowjustcreatedadirectoryinyourHomesteadvirtualmachineat/home/vagrant/Sitesthatwillmirroryourcomputer ’sdirectoryat~/Sites.

Page 42: Laravel: Up and Running: A Framework for Building Modern PHP

TOP-LEVELDOMAINSFORDEVELOPMENTSITESYoucanchooseanyconventionforlocaldevelopmentsites’URLs,but.appand.devarethemostcommon.Homesteadsuggests.app,soifI’mworkingonalocalcopyofsymposiumapp.com,I’lldevelopatsymposiumapp.app.

Now,let’ssetupourfirstexamplewebsite.Let’ssayourlivesiteisgoingtobeprojectName.com.InHomestead.yaml,we’llmapourlocaldevelopmentfoldertoprojectName.app,sowehaveaseparateURLtovisitforlocaldevelopment:

sites:

-map:projectName.app

to:/home/vagrant/Sites/projectName/public

Asyoucansee,we’remappingtheURLprojectName.apptothevirtualmachinedirectory/home/vagrant/Sites/projectName/public,whichisthepublicfolderwithinourLaravelinstall.We’lllearnmoreaboutthatlater.

Finally,you’regoingtoneedtoteachyourlocalmachinethat,whenyoutrytovisitprojectName.app,itshouldlookatyourcomputer ’slocalIPaddresstoresolveit.MacandLinuxusersshouldedit/etc/hosts,andWindowsusersC:\Windows\System32\drivers\etc\hosts.Addalinetothisfilethatlookslikethis:

192.168.10.10projectName.app

Onceyou’veprovisionedHomestead,yoursitewillbeavailabletobrowse(onyourmachine)athttp://projectName.app/.

CreatingdatabasesinHomesteadJustlikeyoucandefineasiteinHomestead.yaml,youcanalsodefineadatabase.Databasesarealotsimpler,becauseyou’reonlytellingtheprovisionertocreateadatabasewiththatname,nothingelse.Wedothisasfollows:

databases:

-projectname

ProvisioningHomesteadThefirsttimeyouactuallyturnonaHomesteadbox,youneedtotellVagranttoinitializeit.NavigatetoyourHomesteaddirectoryandrunvagrantup:

cd~/Homestead

vagrantup

YourHomesteadboxisnowupandrunning;it’smirroringalocalfolder,andit’sservingittoaURLyoucanvisitinanybrowseronyourcomputer.ItalsohascreatedaMySQL

Page 43: Laravel: Up and Running: A Framework for Building Modern PHP

database.Nowthatyouhavethatenvironmentrunning,you’rereadytosetupyourfirstLaravelproject—butfirst,aquicknoteaboutusingHomesteadday-to-day.

UsingHomesteadday-to-dayIt’scommontoleaveyourHomesteadvirtualmachineupandrunningatalltimes,butifyoudon’t,orifyouhaverecentlyrestartedyourcomputer,you’llneedtoknowhowtospintheboxupanddown.

SinceHomesteadisbasedonVagrantcommands,you’lljustusebasicVagrantcommandsformostHomesteadactions.ChangetothedirectorywhereyouinstalledHomestead(usingcd)andthenrunthefollowingcommandsasneeded:

vagrantupspinsuptheHomesteadbox.

vagrantsuspendtakesasnapshotofwheretheboxisandthenshutsitdown;like“hibernating”adesktopmachine.

vagranthaltshutstheentireboxdown;liketurningoffadesktopmachine.

vagrantdestroydeletestheentirebox;likeformattingadesktopmachine.

vagrantprovisionre-runstheprovisionersonthepreexistingbox.

ConnectingtoHomesteaddatabasesfromdesktopapplicationsIfyouuseadesktopapplicationlikeSequelPro,you’lllikelywanttoconnecttoyourHomesteadMySQLdatabasesfromyourhostmachine.Thesesettingswillgetyougoing:

Connectiontype:Standard(non-SSH)

Host:127.0.0.1

Username:homestead

Password:secret

Port:33060

Page 44: Laravel: Up and Running: A Framework for Building Modern PHP

CreatingaNewLaravelProjectTherearetwowaystocreateanewLaravelproject,butbotharerunfromthecommandline.ThefirstoptionistogloballyinstalltheLaravelinstallertool(usingComposer);thesecondistouseComposer ’screate-projectfeature.

YoucanlearnaboutbothoptionsinmoredetailontheInstallationdocumentationpage,butI’drecommendtheLaravelinstallertool.

Page 45: Laravel: Up and Running: A Framework for Building Modern PHP

InstallingLaravelwiththeLaravelInstallerToolIfyouhaveComposerinstalledglobally,installingtheLaravelinstallertoolisassimpleasrunningthefollowingcommand:

composerglobalrequire"laravel/installer=~1.1"

OnceyouhavetheLaravelinstallertoolinstalled,spinningupanewLaravelprojectissimple.Justrunthiscommandfromyourcommandline:

laravelnewprojectName

ThiswillcreateanewsubdirectoryofyourcurrentdirectorynamedprojectNameandinstallabareLaravelprojectinit.

Page 46: Laravel: Up and Running: A Framework for Building Modern PHP

InstallingLaravelwithComposer’screate-projectFeatureComposeralsooffersafeaturecalledcreate-projectforcreatingnewprojectswithaparticularskeleton.TousethistooltocreateanewLaravelproject,issuethefollowingcommand:

composercreate-projectlaravel/laravelprojectName--prefer-dist

Justliketheinstallertool,thiswillcreateasubdirectoryofyourcurrentdirectorynamedprojectNamethatcontainsaskeletonLaravelinstall,readyforyoutodevelop.

Page 47: Laravel: Up and Running: A Framework for Building Modern PHP

Laravel’sDirectoryStructureWhenyouopenupadirectorythatcontainsaskeletonLaravelapplication,you’llseethefollowingfilesanddirectories:

app/

bootstrap/

config/

database/

public/

resources/

routes/

storage/

tests/

vendor/

.env

.env.example

.gitattributes

.gitignore

artisan

composer.json

composer.lock

gulpfile.js

package.json

phpunit.xml

readme.md

server.php

Let’swalkthroughthemonebyonetogetfamiliar.

Page 48: Laravel: Up and Running: A Framework for Building Modern PHP

TheFoldersTherootdirectorycontainsthefollowingfoldersbydefault:

appiswherethebulkofyouractualapplicationwillgo.Models,controllers,routedefinitions,commands,andyourPHPdomaincodeallgoinhere.

bootstrapcontainsthefilesthattheLaravelframeworkusestobooteverytimeitruns.

configiswherealltheconfigurationfileslive.

databaseiswheredatabasemigrationsandseedslive.

publicisthedirectorytheserverpointstowhenit’sservingthewebsite.Thiscontainsindex.php,whichisthefrontcontrollerthatkicksoffthebootstrappingprocessandroutesallrequestsappropriately.It’salsowhereanypublic-facingfileslikeimages,stylesheets,scripts,ordownloadsgo.

resourcesiswherenon-PHPfilesthatareneededforotherscriptslive.Views,languagefiles,and(optionally)Sass/LESSandsourceJavaScriptfileslivehere.

routesiswherealloftheroutedefinitionslive,bothforHTTProutesand“consoleroutes,”orArtisancommands.

storageiswherecaches,logs,andcompiledsystemfileslive.

testsiswhereunitandintegrationtestslive.

vendoriswhereComposerinstallsitsdependencies.It’sGit-ignored(markedtobeexcludedfromyourversioncontrolsystem),asComposerisexpectedtorunasapartofyourdeployprocessonanyremoteservers.

Page 49: Laravel: Up and Running: A Framework for Building Modern PHP

TheLooseFilesTherootdirectoryalsocontainsthefollowingfiles:

.envand.env.examplearethefilesthatdictatetheenvironmentvariables(variablesthatareexpectedtobedifferentineachenvironmentandarethereforenotcommittedtoversioncontrol)..env.exampleisatemplatethateachenvironmentshouldduplicatetocreateitsown.envfile,whichisGit-ignored.

artisanisthefilethatallowsyoutorunArtisancommands(seeChapter7)fromthecommandline.

.gitignoreand.gitattributesareGitconfigurationfiles.

composer.jsonandcomposer.lockaretheconfigurationfilesforComposer;composer.jsonisuser-editableandcomposer.lockisnot.ThesefilessharesomebasicinformationaboutthisprojectandalsodefineitsPHPdependencies.

gulpfile.jsisthe(optional)configurationfileforElixirandGulp.Thisisforcompilingandprocessingyourfrontendassets.

package.jsonislikecomposer.jsonbutforfrontendassets.

phpunit.xmlisaconfigurationfileforPHPUnit,thetoolLaravelusesfortestingoutofthebox.

readme.mdisaMarkdownfilegivingabasicintroductiontoLaravel.

server.phpisabackupserverthattriestoallowless-capableserverstostillpreviewtheLaravelapplication.

Page 50: Laravel: Up and Running: A Framework for Building Modern PHP

ConfigurationThecoresettingsofyourLaravelapplication—databaseconnection,queueandmailsettings,etc.—liveinfilesintheconfigfolder.Eachofthesefilesreturnsanarray,andeachvalueinthearraywillbeaccessiblebyaconfigkeythatiscomprisedofthefilenameandalldescendantkeys,separatedbydots(.)

So,ifyoucreateafileatconfig/services.phpthatlookslikethis:

//config/services.php

return[

'sparkpost'=>[

'secret'=>'abcdefg'

]

];

youwillnowhaveaccesstothatconfigvariableusingconfig('services.sparkpost.secret').

Anyconfigurationvariablesthatshouldbedistinctforeachenvironment(andthereforenotcommittedtosourcecontrol)willinsteadliveinyour.envfiles.Let’ssayyouwanttouseadifferentBugsnagAPIkeyforeachenvironment.You’dsettheconfigfiletopullitfrom.env:

//config/services.php

return[

'bugsnag'=>[

'api_key'=>env('BUGSNAG_API_KEY')

]

];

Thisenv()helperfunctionpullsavaluefromyour.envfilewiththatsamekey.Sonow,addthatkeytoyour.env(settingsforthisenvironment)and.env.example(templateforallenvironments)files:

BUGSNAG_API_KEY=oinfp9813410942

Your.envfilealreadycontainsquiteafewenvironment-specificvariablesneededbytheframework,likewhichmaildriveryou’llbeusingandwhatyourbasicdatabasesettingsare.

Page 51: Laravel: Up and Running: A Framework for Building Modern PHP

UpandRunningYou’renowupandrunningwithabareLaravelinstall.Rungitinit,committhebarefileswithgitadd.andgitcommit,andyou’rereadytostartcoding.That’sit!Andifyou’reusingValet,youcanrunthefollowingcommandsandinstantlyseeyoursiteliveinyourbrowser:

laravelnewmyProject&&cdmyProject&&valetopen

EverytimeIstartanewproject,thesearethestepsItake:

laravelnewmyProject

cdmyProject

gitinit

gitadd.

gitcommit-m"Initialcommit"

Ikeepallofmysitesina~/Sitesfolder,whichIhavesetupasmyprimaryValetdirectory,sointhiscaseI’dinstantlyhavemyProject.devaccessibleinmybrowserwithnoaddedwork.Icanedit.envandpointittoaparticulardatabase,addthatdatabaseinmyMySQLapp,andI’mreadytostartcoding.

Page 52: Laravel: Up and Running: A Framework for Building Modern PHP
Page 53: Laravel: Up and Running: A Framework for Building Modern PHP

LAMBOIperformthethissetofstepssooftenthatIcreatedasimpleglobalComposerpackagetodoitforme.It’scalledLambo,andyoucanlearnmoreaboutitonGitHub.

Page 54: Laravel: Up and Running: A Framework for Building Modern PHP

TestingIneverychapterafterthis,the“Testing”sectionattheendofthechapterwillshowyouhowtowritetestsforthefeatureorfeaturesthatwerecovered.Sincethischapterdoesn’tcoveratestablefeature,let’stalktestsquickly.(TolearnmoreaboutwritingandrunningtestsinLaravel,headovertoChapter12.)

Outofthebox,LaravelbringsinPHPUnitasadependencyandisconfiguredtorunthetestsinanyfileinthetestsdirectorywhosenameendswithTest.php(forexample,tests/UserTest.php).

So,thesimplestwaytowritetestsistocreateafileinthetestsdirectorywithanamethatendswithTest.php.Andtheeasiestwaytorunthemistorun./vendor/bin/phpunitfromthecommandline(intheprojectroot).

Ifanytestsrequiredatabaseaccess,besuretorunyourtestsfromthemachinewhereyourdatabaseishosted—soifyou’rehostingyourdatabaseinVagrant,makesuretosshintoyourVagrantboxtorunyourtestsfromthere.Again,youcanlearnaboutthisandmuchmoreinChapter12.

Page 55: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRSinceLaravelisaPHPframework,it’sverysimpletoserveitlocally.Laravelalsoprovidestwotoolsformanagingyourlocaldevelopment:asimplertoolcalledValetthatusesyourlocalmachinetoprovideyourdependencies,andapreconfiguredVagrantsetupnamedHomestead.Laravelrelieson,andcanbeinstalledby,Composer,andcomesoutoftheboxwithaseriesoffoldersandfilesthatreflectbothitsconventionsanditsrelationshipwithotheropensourcetools.

Page 56: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter3.RoutingandControllers

Theessentialfunctionofanywebapplicationframeworkistotakerequestsfromauseranddeliverresponses,usuallyviaHTTP(S).Thismeansdefininganapplication’sroutesisthefirstandmostimportantprojecttotacklewhenlearningawebframework;withoutroutes,youhavenoabilitytointeractwiththeenduser.

InthischapterwewillexamineroutesinLaravelandshowhowtodefinethem,howtopointthemtothecodetheyshouldexecute,andhowtouseLaravel’sroutingtoolstohandleadiversearrayofroutingneeds.

Page 57: Laravel: Up and Running: A Framework for Building Modern PHP

RouteDefinitionsInaLaravelapplication,youwilldefineyour“web”routesinroutes/web.phpandyour“API”routesinroutes/api.php.Webroutesarethosethatwillbevisitedbyyourendusers;APIroutesarethoseforyourAPI,ifyouhaveone.Fornow,we’llprimarilyfocusontheroutesinroutes/web.php.

NOTEInprojectsrunningversionsofLaravelpriorto5.3,therewillbeonlyoneroutesfile,locatedatapp/Http/routes.php.

Thesimplestwaytodefinearouteistomatchapath(e.g.,/)withaclosure,asseeninExample3-1.

Example3-1.Basicroutedefinition//routes/web.php

Route::get('/',function(){

return'Hello,World!';

});

WHAT’SACLOSURE?

ClosuresarePHP’sversionofanonymousfunctions.Aclosureisafunctionthatyoucanpassaroundasanobject,assigntoavariable,passasaparametertootherfunctionsandmethods,orevenserialize.

You’venowdefinedthat,ifanyonevisits/(therootofyourdomain),Laravel’sroutershouldruntheclosuredefinedthereandreturntheresult.Notethatwereturnourcontentanddon’techoorprintit.

Page 58: Laravel: Up and Running: A Framework for Building Modern PHP
Page 59: Laravel: Up and Running: A Framework for Building Modern PHP

AQUICKINTRODUCTIONTOMIDDLEWAREYoumightbewondering,“WhyamIreturning‘Hello,World!’insteadofechoingit?”

Therearequiteafewanswers,butthesimplestisthattherearealotofwrappersaroundLaravel’srequestandresponsecycle,includingsomethingcalledmiddleware.Whenyourrouteclosureorcontrollermethodisdone,it’snottimetosendtheoutputtothebrowseryet;returningthecontentallowsittocontinueflowingthroughtheresponsestackandthemiddlewarebeforeitisreturnedbacktotheuser.

Manysimplewebsitescouldbedefinedentirelywithinthewebroutesfile.WithafewsimpleGETroutescombinedwithsometemplatesasillustratedinExample3-2,youcancanserveaclassicwebsiteeasily.

Example3-2.SamplewebsiteRoute::get('/',function(){

returnview('welcome');

});

Route::get('about',function(){

returnview('about');

});

Route::get('products',function(){

returnview('products');

});

Route::get('services',function(){

returnview('services');

});

Page 60: Laravel: Up and Running: A Framework for Building Modern PHP
Page 61: Laravel: Up and Running: A Framework for Building Modern PHP

STATICCALLSIfyouhavemuchexperiencedevelopingPHP,youmightbesurprisedtoseestaticcallsontheRouteclass.Thisisnotactuallyastaticmethodperse,butratherservicelocationusingLaravel’sfacades,whichwe’llcoverinChapter11.

Ifyouprefertoavoidfacades,youcanaccomplishthesesamedefinitionslikethis:

$router->get('/',function(){

return'Hello,World!';

});

HTTPMETHODS

Ifyou’renotfamiliarwiththeideaofHTTPmethods,readoninthischapterformoreinformation,butfornow,justknowthateveryHTTPrequesthasa“verb,”oraction,alongwithit.Laravelallowsyoutodefineyourroutesbasedonwhichverbwasused;themostcommonareGETandPOST,followedbyPUT,DELETE,andPATCH.Eachmethodcommunicatesadifferentthingtotheserver,andtoyourcode,abouttheintentionsofthecaller.

Page 62: Laravel: Up and Running: A Framework for Building Modern PHP

RouteVerbsYoumight’venoticedthatwe’vebeenusingRoute::getinourroutedefinitions.Thismeanswe’retellingLaraveltoonlymatchfortheserouteswhentheHTTPrequestusestheGETaction.Butwhatifit’saformPOST,ormaybesomeJavaScriptsendingPUTorDELETErequests?Thereareafewotheroptionsformethodstocallonaroutedefinition,asillustratedinExample3-3.

Example3-3.RouteverbsRoute::get('/',function(){

return'Hello,World!';

});

Route::post('/',function(){});

Route::put('/',function(){});

Route::delete('/',function(){});

Route::any('/',function(){});

Route::match(['get','post'],'/',function(){});

Page 63: Laravel: Up and Running: A Framework for Building Modern PHP

RouteHandlingAsyou’veprobablyguessed,passingaclosuretotheroutedefinitionisnottheonlywaytoteachithowtoresolvearoute.Closuresarequickandsimple,butthelargeryourapplicationgets,theclumsieritbecomestoputallofyourroutinglogicinonefile.Additionally,applicationsusingrouteclosurescan’ttakeadvantageofLaravel’sroutecaching(moreonthatlater),whichcanshaveuptohundredsofmillisecondsoffofeachrequest.

Theothercommonoptionistopassacontrollernameandmethodasastringinplaceoftheclosure,asinExample3-4.

Example3-4.RoutescallingcontrollermethodsRoute::get('/','WelcomeController@index');

ThisistellingLaraveltopassrequeststothatpathtotheindex()methodoftheApp\Http\Controllers\WelcomeControllercontroller.Thismethodwillbepassedthesameparametersandtreatedthesamewayasaclosureyoumight’vealternativelyputinitsplace.

Page 64: Laravel: Up and Running: A Framework for Building Modern PHP

RouteParametersIftherouteyou’redefininghasparameters—segmentsintheURLstructurethatarevariable—it’ssimpletodefinetheminyourrouteandpassthemtoyourclosure(seeExample3-5).

Example3-5.RouteparametersRoute::get('users/{id}/friends',function($id){

//

});

THENAMINGRELATIONSHIPBETWEENROUTEPARAMETERSANDCLOSURE/CONTROLLER METHODPARAMETERS

AsyoucanseeinExample3-5,it’smostcommontousethesamenamesforyourrouteparameters({id})andthemethodparameterstheyinjectintoyourroutedefinition(function($id)).Butisthisnecessary?

Unlessyou’reusingroute/modelbinding,no.Theonlythingthatdefineswhichrouteparametermatcheswithwhichmethodparameteristheirorder(lefttoright),asyoucanseehere:

Route::get('users/{userId}/comments/{commentId}',function(

$thisIsActuallyTheRouteId,

$thisisReallyTheCommentId

){

//

});

Thathavingbeensaid,justbecauseyoucanmakethemdifferentdoesn’tmeanyoushould.Irecommendkeepingthemthesameforthesakeoffuturedevelopers,whocouldgettrippedupbyinconsistentnaming.

Youcanalsomakeyourrouteparametersoptionalbyincludingaquestionmark(?)aftertheparametername,asillustratedinExample3-6.Inthiscase,youshouldalsoprovideadefaultvaluefortheroute’scorrespondingvariable.

Example3-6.OptionalrouteparametersRoute::get('users/{id?}',function($id='fallbackId'){

//

});

Andyoucanuseregularexpressions(regexes)todefinethatarouteshouldonlymatchifaparametermeetsparticularrequirements,asinExample3-7.

Example3-7.RegularexpressionrouteconstraintsRoute::get('users/{id}',function($id){

//

})->where('id','[0-9]+');

Route::get('users/{username}',function($username){

//

})->where('username','[A-Za-z]+');

Route::get('posts/{id}/{slug}',function($id,$slug){

//

})->where(['id'=>'[0-9]+','slug'=>'[A-Za-z]+']);

Asyou’veprobablyguessed,ifyouvisitapaththatmatchesaroutestring,buttheregexdoesn’tmatchtheparameter,itwon’tbematched.Sinceroutesarematchedtoptobottom,

Page 65: Laravel: Up and Running: A Framework for Building Modern PHP

users/abcwouldskipthefirstclosureinExample3-7,butitwouldbematchedbythesecondclosure,soitwouldgetroutedthere.Ontheotherhand,posts/abc/123wouldn’tmatchanyoftheclosures,soitwouldreturna404NotFounderror.

Page 66: Laravel: Up and Running: A Framework for Building Modern PHP

RouteNamesThesimplestwaytorefertotheserouteselsewhereinyourapplicationisjustbytheirpath.There’saurl()helpertosimplifythatlinkinginyourviews,ifyouneedit;seeExample3-8foranexample.Thehelperwillprefixyourroutewiththefulldomainofyoursite.

Example3-8.URLhelper<ahref="<?phpechourl('/');?>">

//outputs<ahref="http://myapp.com/">

However,Laravelalsoallowsyoutonameeachroute,whichenablesyoutorefertoitwithoutexplicitlyreferencingtheURL.Thisishelpfulbecauseitmeansyoucangivesimplenicknamestocomplexroutes,andalsobecauselinkingthembynamemeansyoudon’thavetorewriteyourfrontendlinksifthepathschange(seeExample3-9).

Example3-9.Definingroutenames//Definingaroutewithnameinroutes/web.php:

Route::get('members/{id}','MembersController@show')->name('members.show');

//Linktherouteinaviewusingtheroute()helper

<ahref="<?phpechoroute('members.show',['id'=>14]);?>">

Thisexampleillustratesafewnewconcepts.First,we’reusingfluentroutedefinitiontoaddthename,bychainingthename()methodaftertheget()method.Thismethodallowsustonametheroute,givingitashortaliastomakeiteasiertoreferenceelsewhere.

Page 67: Laravel: Up and Running: A Framework for Building Modern PHP
Page 68: Laravel: Up and Running: A Framework for Building Modern PHP

DEFININGCUSTOMROUTESINLARAVEL5.1Fluentroutedefinitionsdon’texistinLaravel5.1.You’llneedtoinsteadpassanarraytothesecondparameterofyourroutedefinition;checktheLaraveldocstoseemoreabouthowthisworks.Here’sExample3-9inLaravel5.1:

Route::get('members/{id}',[

'as'=>'members.show',

'uses'=>'MembersController@show'

]);

Inourexample,we’venamedthisroutemembers.show;resourcePlural.actionisacommonconventionwithinLaravelforrouteandviewnames.

ROUTENAMINGCONVENTIONS

Youcannameyourrouteanythingyou’dlike,butthecommonconventionistousethepluraloftheresourcename,thenaperiod,thentheaction.So,herearetheroutesmostcommonforaresourcenamedphoto:

photos.index

photos.create

photos.store

photos.show

photos.edit

photos.update

photos.destroy

Tolearnmoreabouttheseconventions,see“ResourceControllers”.

Wealsointroducedtheroute()helper.Justlikeurl(),it’sintendedtobeusedinviewstosimplifylinkingtoanamedroute.Iftheroutehasnoparameters,youcansimplypasstheroutename:(route('members.index'))andreceivearoutestringhttp://myapp.com/members/index).Ifithasparameters,passtheminasanarrayasthesecondparameterlikewedidinthisexample.

Ingeneral,Irecommendusingroutenamesinsteadofpathstorefertoyourroutes,andthereforeusingtheroute()helperinsteadoftheurl()helper.Sometimesitcangetabitclumsy—forexample,ifyou’reworkingwithmultiplesubdomains—butitprovidesanincrediblelevelofflexibilitytolaterchangetheapplication’sroutingstructurewithoutmajorpenalty.

Page 69: Laravel: Up and Running: A Framework for Building Modern PHP

PASSINGROUTEPARAMETERSTOTHEROUTE( ) HELPER

Whenyourroutehasparameters(e.g.,users/{id}),youneedtodefinethoseparameterswhenyou’reusingtheroute()helpertogeneratealinktotheroute.

Thereareafewdifferentwaystopasstheseparameters.Let’simaginearoutedefinedasusers/{userId}/comments/{commentId}.IftheuserIDis1andthecommentIDis2,let’slookatafewoptionswehaveavailabletous:

Option1:

route('users.comments.show',[1,2])

//http://myapp.com/users/1/comments/2

Option2:

route('users.comments.show',['userId'=>1,'commentId'=>2])

//http://myapp.com/users/1/comments/2

Option3:

route('users.comments.show',['commentId'=>2,'userId'=>1])

//http://myapp.com/users/1/comments/2

Option4:

route('users.comments.show',['userId'=>1,'commentId'=>2,'opt'=>'a'])

//http://myapp.com/users/1/comments/2?opt=a

Asyoucansee,nonkeyedarrayvaluesareassignedinorder;keyedarrayvaluesarematchedwiththerouteparametersmatchingtheirkeys,andanythingleftoverisaddedasaqueryparameter.

Page 70: Laravel: Up and Running: A Framework for Building Modern PHP

RouteGroupsOftenagroupofroutesshareaparticularcharacteristic—acertainauthenticationrequirement,apathprefix,orperhapsacontrollernamespace.Definingthesesharedcharacteristicsagainandagainoneachroutenotonlyseemstediousbutalsocanmuddyuptheshapeofyourroutesfileandobscuresomeofthestructuresofyourapplication.

Routegroupsallowyoutogroupseveralroutestogether,andapplyanysharedconfigurationsettingsoncetotheentiregroup,toreducethisduplication.Additionally,routegroupsarevisualcuestofuturedevelopers(andtoyourownbrain)thattheseroutesaregroupedtogether.

Togrouptwoormoreroutestogether,you“surround”theroutedefinitionswitharoutegroup,asshowninExample3-10.Inreality,you’reactuallypassingaclosuretothegroupdefinition,anddefiningthegroupedrouteswithinthatclosure.

Example3-10.DefiningaroutegroupRoute::group([],function(){

Route::get('hello',function(){

return'Hello';

});

Route::get('world',function(){

return'World';

});

});

Bydefault,aroutegroupdoesn’tactuallydoanything.There’snodifferencebetweenthegroupinExample3-10andseparatingasegmentofyourrouteswithcodecomments.Theemptyarraythat’sthefirstparameter,however,allowsyoutopassavarietyofconfigurationsettingsthatwillapplytotheentireroutegroup.

Page 71: Laravel: Up and Running: A Framework for Building Modern PHP

MiddlewareProbablythemostcommonuseforroutegroupsistoapplymiddlewaretoagroupofroutes.We’lllearnmoreaboutmiddlewareinChapter10,but,amongotherthings,they’rewhatLaravelusesforauthenticatingusersandrestrictingguestusersfromusingcertainpartsofasite.

InExample3-11,we’recreatingaroutegrouparoundthedashboardandaccountviewsandapplyingtheauthmiddlewaretoboth.Inthisexample,itmeansusershavetobeloggedintotheapplicationtoviewthedashboardortheaccountpage.

Example3-11.Restrictingagroupofroutestologged-inusersonlyRoute::group(['middleware'=>'auth'],function(){

Route::get('dashboard',function(){

returnview('dashboard');

});

Route::get('account',function(){

returnview('account');

});

});

Page 72: Laravel: Up and Running: A Framework for Building Modern PHP
Page 73: Laravel: Up and Running: A Framework for Building Modern PHP

APPLYINGMIDDLEWAREINCONTROLLERSOftenit’sclearerandmoredirecttoattachmiddlewaretoyourroutesinthecontrollerinsteadofattheroutedefinition.Youcandothisbycallingthemiddleware()methodintheconstructorofyourcontroller.Thestringyoupasstothemiddleware()methodisthenameofthemiddleware,andyoucanoptionallychainmodifiermethods(only()andexcept())todefinewhichmethodswillreceivethatmiddleware:

classDashboardControllerextendsController

{

publicfunction__construct()

{

$this->middleware('auth');

$this->middleware('admin-auth')

->only('admin');

$this->middleware('team-member')

->except('admin');

}

}

Notethat,ifyou’redoingalotof“only”and“except”customizations,that’softenasignthatyoushouldbreakoutanewcontrollerfortheexceptionalroutes.

Page 74: Laravel: Up and Running: A Framework for Building Modern PHP

PathPrefixesIfyouhaveagroupofroutesthatshareasegmentoftheirpath—forexample,ifyoursite’sAPIisprefixedwith/api—youcanuseroutegroupstosimplifythisstructure(seeExample3-12).

Example3-12.PrefixingagroupofroutesRoute::group(['prefix'=>'api'],function(){

Route::get('/',function(){

//Handlesthepath/api

});

Route::get('users',function(){

//Handlesthepath/api/users

});

});

Notethateachprefixedgroupalsohasa/routethatrepresentstherootoftheprefix—inExample3-12that’s/api.

Page 75: Laravel: Up and Running: A Framework for Building Modern PHP

SubdomainRoutingSubdomainroutingisthesameasrouteprefixing,butit’sscopedbysubdomaininsteadofrouteprefix.Therearetwoprimaryusesforthis.First,youmaywanttopresentdifferentsectionsoftheapplication(orentirelydifferentapplications)todifferentsubdomains.Example3-13showshowyoucanachievethis.

Example3-13.SubdomainroutingRoute::group(['domain'=>'api.myapp.com'],function(){

Route::get('/',function(){

//

});

});

Second,youmightwanttosetpartofthesubdomainasaparameter,asillustratedinExample3-14.Thisismostoftendoneincasesofmultitenancy(thinkSlackorHarvest,whereeachcompanygetsitsownsubdomain,liketighten.slack.co).

Example3-14.ParameterizedsubdomainroutingRoute::group(['domain'=>'{account}.myapp.com'],function(){

Route::get('/',function($account){

//

});

Route::get('users/{id}',function($account,$id){

//

});

});

Notethatanyparametersforthegroupgetpassedintothegroupedroutes’methodsasthefirstparameter(s).

Page 76: Laravel: Up and Running: A Framework for Building Modern PHP

NamespacePrefixesWhenyou’regroupingroutesbysubdomainorrouteprefix,it’slikelytheircontrollershaveasimilarPHPnamespace.IntheAPIexample,alloftheAPIroutes’controllersmightbeunderanAPInamespace.Byusingtheroutegroupnamespaceprefix,asshowninExample3-15,youcanavoidlongcontrollerreferencesingroupslike"API/ControllerA@index"and"API/ControllerB@index".

Example3-15.Routegroupnamespaceprefixes//App\Http\Controllers\ControllerA

Route::get('/','ControllerA@index');

Route::group(['namespace'=>'API'],function(){

//App\Http\Controllers\API\ControllerB

Route::get('api/','ControllerB@index');

});

Page 77: Laravel: Up and Running: A Framework for Building Modern PHP

NamePrefixesTheprefixesdon’tstopthere.It’scommonthatroutenameswillreflecttheinheritancechainofpathelements,sousers/comments/5willbeservedbyaroutenamedusers.comments.show.Inthiscase,it’scommontousearoutegrouparoundalloftheroutesthatarebeneaththeusers.commentsresource.

JustlikewecanprefixURLsegmentsandcontrollernamespaces,wecanalsoprefixstringstotheroutename.Withroutegroupnameprefixes,wecandefinethateveryroutewithinthisgroupshouldhaveagivenstringprefixedtoitsname.Inthiscontext,we’reprefixing"users."toeachroutename,then"comments."(seeExample3-16).

Example3-16.RoutegroupnameprefixesRoute::group(['as'=>'users.','prefix'=>'users'],function(){

Route::group(['as'=>'comments.','prefix'=>'comments'],function(){

//Routenamewillbeusers.comments.show

Route::get('{id}',function(){

//

})->name('show');

});

});

Page 78: Laravel: Up and Running: A Framework for Building Modern PHP

ViewsInafewoftherouteclosureswe’velookedatsofar,we’veseensomethingalongthelinesofreturnview('account').What’sgoingonhere?

Ifyou’renotfamiliarwiththeModel–View–Controller(MVC)pattern,views(ortemplates)arefilesthatdescribewhatsomeparticularoutputshouldlooklike.YoumighthaveviewsforJSONorXMLoremails,butthemostcommonviewsinawebframeworkoutputHTML.

InLaravel,therearetwoformatsofviewyoucanuseoutofthebox:plainPHP,orBladetemplates(seeChapter4).Thedifferenceisinthefilename:about.phpwillberenderedwiththePHPengine,andabout.blade.phpwillberenderedwiththeBladeengine.

Page 79: Laravel: Up and Running: A Framework for Building Modern PHP
Page 80: Laravel: Up and Running: A Framework for Building Modern PHP

THREEWAYSTOLOADAVIEW()Therearethreedifferentwaystoreturnaview.Fornow,justconcernyourselfwithview(),butifyoueverseeView::make(),it’sthesamething,andyoucouldalsoinjecttheIlluminate\View\ViewFactoryifyouprefer.

Onceyou’veloadedaview,youhavetheoptiontosimplyreturnit(asinExample3-17),whichwillworkfineiftheviewdoesn’trelyonanyvariablesfromthecontroller.

Example3-17.Simpleview()usageRoute::get('/',function(){

returnview('home');

});

Thiscodelooksforaviewinresources/views/home.blade.phporresources/views/home.php,andloadsitscontentsandparsesanyinlinePHPorcontrolstructuresuntilyouhavejusttheview’soutput.Onceyoureturnit,it’spassedontotherestoftheresponsestackandeventuallyreturnedtotheuser.

Butwhatifyouneedtopassinvariables?TakealookatExample3-18.

Example3-18.PassingvariablestoviewsRoute::get('tasks',function(){

returnview('tasks.index')

->with('tasks',Task::all());

});

Thisclosureloadstheresources/views/tasks/index.blade.phporresources/views/tasks/index.phpviewandpassesitasinglevariablenamedtasks,whichcontainstheresultoftheTask::all()method.Task::all()isanEloquentdatabasequerywe’lllearnaboutinChapter8.

Page 81: Laravel: Up and Running: A Framework for Building Modern PHP

UsingViewComposerstoShareVariableswithEveryViewSometimesitcanbecomeahassletopassthesamevariablesoverandover.Theremaybeavariablethatyouwantaccessibletoeveryviewinthesite,ortoacertainclassofviewsoracertainincludedsubview—forexample,allviewsrelatedtotasks,ortheheaderpartial.

It’spossibletosharecertainvariableswitheverytemplateorjustcertaintemplates,likeinthefollowingcode:

view()->share('variableName','variableValue');

Tolearnmore,checkout“ViewComposersandServiceInjection”.

Page 82: Laravel: Up and Running: A Framework for Building Modern PHP

ControllersI’vementionedcontrollersafewtimes,butuntilnowmostoftheexampleshaveshownrouteclosures.Ifyou’renotfamiliarwiththeMVCpattern(Figure3-1),controllersareessentiallyclassesthatorganizethelogicofoneormoreroutestogetherinoneplace.Controllerstendtogroupsimilarroutestogether,especiallyifyourapplicationisstructuredalongatraditionallyCRUD-likeformat;inthiscase,acontrollermighthandlealltheactionsthatcanbeperformedonaparticularresource.

Figure3-1.AbasicillustrationofMVC

Page 83: Laravel: Up and Running: A Framework for Building Modern PHP
Page 84: Laravel: Up and Running: A Framework for Building Modern PHP

WHATISCRUD?CRUDstandsforcreate,read,update,delete,whicharethefourprimaryoperationsthatwebapplicationsmostcommonlyprovideonaresource.Forexample,youcancreateanewblogpost,youcanreadthatpost,youcanupdateit,oryoucandeleteit.

Itmaybetemptingtocramalloftheapplication’slogicintothecontrollers,butit’sbettertothinkofcontrollersasthetrafficcopsthatrouteHTTPrequestsaroundyourapplication.Sincethereareotherwaysrequestscancomeintoyourapplication—cronjobs,Artisancommand-linecalls,queuejobs,etc.—it’swisetonotrelyoncontrollersformuchbehavior.Thismeansacontroller ’sprimaryjobistocapturetheintentofanHTTPrequestandpassitontotherestoftheapplication.

So,let’screateacontroller.OneeasywaytodothisiswithanArtisancommand,sofromthecommandlinerunthefollowing:

phpartisanmake:controllerTasksController

Page 85: Laravel: Up and Running: A Framework for Building Modern PHP
Page 86: Laravel: Up and Running: A Framework for Building Modern PHP

ARTISANANDARTISANGENERATORSLaravelcomesbundledwithacommand-linetoolcalledArtisan.Artisancanbeusedtorunmigrations,createusersandotherdatabaserecordsmanually,andperformmanyothermanual,one-timetasks.

Underthemakenamespace,Artisanprovidestoolsforgeneratingskeletonfilesforavarietyofsystemfiles.That’swhatallowsustorunphpartisanmake:controller.

TolearnmoreaboutthisandotherArtisanfeatures,seeChapter7.

ThiswillcreateanewfilenamedTasksController.phpinapp/Http/Controllers,withthecontentsshowninExample3-19.

Example3-19.Defaultgeneratedcontroller<?php

namespaceApp\Http\Controllers;

useIlluminate\Http\Request;

useApp\Http\Requests;

classTasksControllerextendsController

{

}

ModifythisfileasshowninExample3-20,creatinganewpublicmethodcalledhome().We’lljustreturnsometextthere.

Example3-20.Simplecontrollerexample<?php

useApp\Http\Controllers\Controller;

classTasksControllerextendsController

{

publicfunctionhome()

{

return'Hello,World!';

}

}

Thenlikewelearnedbefore,we’llhookuparoutetoit,asshowninExample3-21.

Example3-21.Routeforthesimplecontroller//routes/web.php

<?php

Route::get('/','TasksController@home');

That’sit.Visitthe/routeandyou’llseethewords“Hello,World!”

Page 87: Laravel: Up and Running: A Framework for Building Modern PHP

CONTROLLER NAMESPACING

InExample3-21wereferencedacontrollerwiththefullyqualifiedclassnameofApp\Http\Controllers\TasksController,butweonlyusedtheclassname.Thisisn’tbecausewecansimplyreferencecontrollersbytheirclassname.Rather,wecanignoretheApp\Http\Controllers\whenwereferencecontrollers;bydefault,Laravelisconfiguredtolookforcontrollerswithinthatnamespace.

ThismeansthatifyouhaveacontrollerwiththefullyqualifiedclassnameofApp\Http\Controllers\API\ExercisesController,you’dreferenceitinaroutedefinitionasAPI\ExercisesController.

Themostcommonuseofacontrollermethod,then,willbesomethinglikeExample3-22.

Example3-22.Commoncontrollermethodexample//TasksController.php

...

publicfunctionindex()

{

returnview('tasks.index')

->with('tasks',Task::all());

}

Thiscontrollermethodloadstheresources/views/tasks/index.blade.phporresources/views/tasks/index.phpviewandpassesitasinglevariablenamedtasks,whichcontainstheresultoftheTask::all()Eloquentmethod.

Page 88: Laravel: Up and Running: A Framework for Building Modern PHP
Page 89: Laravel: Up and Running: A Framework for Building Modern PHP

GENERATINGRESOURCECONTROLLERSIfyoueverusedphpartisanmake:controllerinLaravelpriorto5.3,youmightbeexpectingittoautogeneratemethodsforallofthebasicresourcerouteslikecreate()andupdate().YoucanbringthisbehaviorbackinLaravel5.3bypassingthe--resourceflagwhenyoucreatethecontroller:

phpartisanmake:controllerTasksController--resource

Page 90: Laravel: Up and Running: A Framework for Building Modern PHP

GettingUserInputThesecondmostcommonactiontoperforminacontrollermethodistotakeinputfromtheuserandactonit.Thatintroducesafewnewconcepts,solet’stakealookatabitofsamplecodeandwalkthroughthenewpieces.

First,let’sbinditquickly;seeExample3-23.

Example3-23.Bindingbasicformactions//routes/web.php

Route::get('tasks/create','TasksController@create');

Route::post('tasks','TasksController@store');

Noticethatwe’rebindingtheGETactionoftasks/create(whichshowstheform)andthePOSTactionoftasks/(whichiswherewePOSTwhenwe’recreatinganewtask).Wecanassumethecreate()methodinourcontrollerjustshowsaform,solet’slookatthestore()methodinExample3-24.

Example3-24.Commonforminputcontrollermethod//TasksController.php

...

publicfunctionstore()

{

$task=newTask;

$task->title=Input::get('title');

$task->description=Input::get('description');

$task->save();

returnredirect('tasks');

}

ThisexamplemakesuseofEloquentmodelsandtheredirect()functionality,andwe’lltalkaboutthemmorelater,butyoucanseewhatwe’redoinghere:wecreateanewTask,pulldataoutoftheuserinputandsetitonthetask,saveit,andthenredirectbacktothepagethatshowsalltasks.

TherearetwomainwaystogetuserinputfromaPOST:theInputfacade,whichweusedhere,andtheRequestobject,whichwe’lltalkaboutnext.

Page 91: Laravel: Up and Running: A Framework for Building Modern PHP

IMPORTINGFACADESIfyoufollowanyoftheseexamples,whetherincontrollersoranyotherPHPclassthatisnamespaced,youmightfinderrorsshowingthatthefacadecannotbefound.Thisisbecausethey’renotpresentineverynamespace,butratherthey’remadeavailableintherootnamespace.

So,inExample3-24,we’dneedtoimporttheInputfacadeatthetopofthefile.Therearetwowaystodothat:eitherwecanimport\Input,orwecanimportIlluminate\Support\Facades\Input.Forexample:

<?php

namespaceApp\Http\Controllers;

useIlluminate\Support\facades\Input;

classTasksController

{

publicfunctionstore()

{

$task=newTask;

$task->title=Input::get('title');

$task->description=Input::get('description');

$task->save();

returnredirect('tasks');

}

Asyoucansee,wecangetthevalueofanyuser-providedinformation,whetherfromaqueryparameteroraPOSTvalue,usingInput::get('fieldName').Soouruserfilledouttwofieldsonthe“addtask”page:“title”and“description.”WeretrievebothusingtheInputfacade,savethemtothedatabase,andthenreturn.

Page 92: Laravel: Up and Running: A Framework for Building Modern PHP

InjectingDependenciesintoControllersLaravel’sfacadespresentasimpleinterfacetothemostusefulclassesinLaravel’scodebase.Youcangetinformationaboutthecurrentrequestanduserinput,thesession,caches,andmuchmore.

Butifyouprefertoinjectyourdependencies,orifyouwanttouseaservicethatdoesn’thaveafacade,you’llneedtofindsomewaytobringinstancesoftheseclassesintoyourcontroller.

ThisisourfirstexposuretoLaravel’sservicecontainer.Fornow,ifthisisunfamiliar,youcanthinkaboutitasalittlebitofLaravelmagic;or,ifyouwanttoknowmoreabouthowit’sactuallyfunctioning,youcanskipaheadtoChapter11.

Allcontrollermethods(includingtheconstructors)areresolvedoutofLaravel’scontainer,whichmeansanythingyoutypehintthatthecontainerknowshowtoresolvewillbeautomaticallyinjected.

Asaniceexample,whatifyou’dpreferhavinganinstanceoftheRequestobjectinsteadofusingthefacade?JusttypehintIlluminate\Http\Requestinyourmethodparameters,likeinExample3-25.

Example3-25.Controllermethodinjectionviatypehinting//TasksController.php

...

publicfunctionstore(\Illuminate\Http\Request$request)

{

$task=newTask;

$task->title=$request->input('title');

$task->description=$request->input('description');

$task->save();

returnredirect('tasks');

}

So,you’vedefinedaparameterthatmustbepassedintothestore()method.Andsinceyoutypehintedit,andsinceLaravelknowshowtoresolvethatclassname,you’regoingtohavetheRequestobjectreadyforyoutouseinyourmethodwithnoworkonyourpart.Noexplicitbinding,noanythingelse—it’sjustthereasthe$requestvariable.

Bytheway,thisisactuallyhowIandmanyotherLaraveldevelopersprefertogettheuserinput:injectaninstanceoftheRequestandreadtheuserinputfromthere,insteadofrelyingontheInputfacade.

Page 93: Laravel: Up and Running: A Framework for Building Modern PHP

ResourceControllersSometimesnamingthemethodsinyourcontrollerscanbethehardestpartofwritingacontroller.Thankfully,LaravelhassomeconventionsforalloftheroutesofatraditionalREST/CRUDcontroller(calleda“resourcecontroller”inLaravel);additionally,itcomeswithageneratoroutoftheboxandaconvenienceroutedefinitionthatallowsyoutobindanentireresourcecontrolleratonce.

ToseethemethodsthatLaravelexpectsforaresourcecontroller,let’sgenerateanewcontrollerfromthecommandline:

phpartisanmake:controllerMySampleResourceController--resource

Nowopenapp/Http/Controllers/MySampleResourceController.php.You’llseeitcomesprefilledwithquiteafewmethods.Let’swalkoverwhateachrepresents.We’lluseaTaskasanexample.

ThemethodsofLaravel’sresourcecontrollersForeach,youcanseetheHTTPverb,theURL,thecontrollermethodname,andthe“name.”Table3-1showstheHTTPverb,theURL,thecontrollermethodname,andthe“name”foreachofthesedefaultmethods.

Table3-1.ThemethodsofLaravel’sresourcecontrollers

Verb URL Controllermethod Name Description

GET tasks index() tasks.index Showalltasks

GET tasks/create create() tasks.create Showthecreatetaskform

POST tasks store() tasks.store Acceptformsubmissionfromthecreatetaskform

GET tasks/{task} show() tasks.show Showonetask

GET tasks/{task}/edit edit() tasks.edit Editonetask

PUT/PATCH tasks/{task} update() tasks.update Acceptformsubmissionfromtheedittaskform

DELETE tasks/{task} destroy() tasks.destroy Deleteonetask

BindingaresourcecontrollerSo,we’veseenthatthesearetheconventionalroutenamestouseinLaravel,andalsothatit’seasytogeneratearesourcecontrollerwithmethodsforeachofthesedefaultroutes.Thankfully,youdon’thavetogenerateroutesforeachofthesecontrollermethodsbyhand,ifyoudon’twantto.Instead,there’satrickforthat,andit’scalled“resourcecontrollerbinding.”TakealookatExample3-26.

Page 94: Laravel: Up and Running: A Framework for Building Modern PHP

Example3-26.Resourcecontrollerbinding//routes/web.php

Route::resource('tasks','TasksController');

Thiswillautomaticallybindalloftheroutesforthisresourcetotheappropriatemethodnamesonthespecifiedcontroller.It’llalsonametheseroutesappropriately;forexample,theindex()methodonthetasksresourcecontrollerwillbenamedtasks.index.

Page 95: Laravel: Up and Running: A Framework for Building Modern PHP
Page 96: Laravel: Up and Running: A Framework for Building Modern PHP

ARTISANROUTE:LISTIfyoueverfindyourselfinasituationwhereyou’rewonderingwhatroutesyourcurrentapplicationhasavailable,there’satoolforthat:fromthecommandline,runphpartisanroute:listandyou’llgetalistingofalloftheavailableroutes(seeFigure3-2).

Figure3-2.phpartisanroute:listexample

Page 97: Laravel: Up and Running: A Framework for Building Modern PHP

RouteModelBindingOneofthemostcommonroutingpatternsisthatthefirstlineofanycontrollermethodtriestofindtheresourcewiththegivenID,likeinExample3-27.

Example3-27.GettingaresourceforeachrouteRoute::get('conferences/{id}',function($id){

$conference=Conference::findOrFail($id);

});

Laravelprovidesafeaturethatsimplifiesthispatterncalled“routemodelbinding.”Thisallowsyoutodefinethataparticularparametername(e.g.,{conference})willindicatetotherouteresolverthatitshouldlookupanEloquentrecordwiththatIDandthenpassitinastheparameterinsteadofjustpassingtheID.

Therearetwokindsofroutemodelbinding:implicitandcustom(orexplicit).

Page 98: Laravel: Up and Running: A Framework for Building Modern PHP

ImplicitRouteModelBindingThesimplestwaytouseroutemodelbindingistonameyourrouteparametersomethinguniquetothatmodel(e.g.,nameit$conferenceinsteadof$id),thentypehintthatparameterintheclosure/controllermethodandusethesamevariablenamethere.It’seasiertoshowthantodescribe,sotakealookatExample3-28.

Example3-28.UsinganexplicitroutemodelbindingRoute::get('conferences/{conference}',function(Conference$conference){

returnview('conferences.show')->with('conference',$conference);

});

Becausetherouteparameter({conference})isthesameasthemethodparameter($conference),andthemethodparameteristypehintedwithaConferencemodel(Conference$conference),Laravelseesthisasaroutemodelbinding.Everytimethisrouteisvisited,theapplicationwillassumethatwhateverispassedintotheURLinplaceof{conference}isanIDthatshouldbeusedtolookupaConference,andthenthatresultingmodelinstancewillbepassedintoyourclosureorcontrollermethod.

Page 99: Laravel: Up and Running: A Framework for Building Modern PHP
Page 100: Laravel: Up and Running: A Framework for Building Modern PHP

CUSTOMIZINGTHEROUTEKEYFORANELOQUENTMODELAnytimeanEloquentmodelislookedupviaaURLsegment(usuallybecauseofroutemodelbinding),thedefaultcolumnEloquentwilllookitupbyisitsprimarykey(ID).

TochangethecolumnyourEloquentmodelusesforURLlookups,addamethodtoyourmodelnamedgetRouteKeyName():

publicfunctiongetRouteKeyName()

{

return'slug';

}

Now,aURLlikeconferences/{conference}willexpecttogetthesluginsteadoftheID,andwillperformitslookupsaccordingly.

ImplicitroutemodelbindingwasaddedinLaravel5.2,soyouwon’thaveaccesstoitin5.1.

Page 101: Laravel: Up and Running: A Framework for Building Modern PHP

CustomRouteModelBindingTomanuallyconfigureroutemodelbindings,addalineliketheoneinExample3-29totheboot()methodinApp\Providers\RouteServiceProvider.

Example3-29.Addingaroutemodelbindingpublicfunctionboot(Router$router)

{

//Justallowstheparent'sboot()methodtostillrun

parent::boot($router);

//Performthebinding

$router->model('event',Conference::class);

}

You’venowdefinedthatwheneveraroutehasaparameterinitsdefinitionnamed{event},asdemonstratedinExample3-30,therouteresolverwillreturnaninstanceoftheConferenceclasswiththeIDofthatURLparameter.

Example3-30.UsinganexplicitroutemodelbindingRoute::get('events/{event}',function(Conference$event){

returnview('events.show')->with('event',$event);

});

Page 102: Laravel: Up and Running: A Framework for Building Modern PHP

RouteCachingIfyou’relookingtosqueezeeverymillisecondoutofyourloadtime,youmaywanttotakealookatroutecaching.OneofthepiecesofLaravel’sbootstrapthatcantakeanywherefromafewdozentoafewhundredmillisecondsisparsingtheroutes/*files,androutecachingspeedsupthisprocessdramatically.

Tocacheyourroutesfile,youneedtobeusingallcontrollerandresourceroutes(norouteclosures).Ifyourappisn’tusinganyrouteclosures,youcanrunphpartisanroute:cache,Laravelwillserializetheresultsofyourroutes/*files.Ifyouwanttodeletethecache,runphpartisanroute:clear.

Here’sthedrawback:Laravelwillnowmatchroutesagainstthatcachedfileinsteadofyouractualroutes/*files.Youcanmakeendlesschangestothosefiles,andtheywon’ttakeeffectuntilyourunroute:cacheagain.Thismeansyou’llhavetorecacheeverytimeyoumakeachange,whichintroducesalotofpotentialforconfusion.

Here’swhatIwouldrecommendinstead:sinceGitignorestheroutecachefilebydefaultanyway,consideronlyusingroutecachingonyourproductionserver,andrunthephpartisanroute:cachecommandeverytimeyoudeploynewcode(whetherviaaGitpost-deployhook,aForgedeploycommand,orasapartofwhateverotherdeploysystemyouuse).Thiswayyouwon’thaveconfusinglocaldevelopmentissues,butyourremoteenvironmentwillstillbenefitfromroutecaching.

Page 103: Laravel: Up and Running: A Framework for Building Modern PHP

FormMethodSpoofingSometimes,youneedtomanuallydefinewhichHTTPverbaformshouldsendas.HTMLformsonlyallowforGETorPOST,soifyouwantanyothersortofverb,you’llneedtospecifythatyourself.

Page 104: Laravel: Up and Running: A Framework for Building Modern PHP

AnIntroductiontoHTTPVerbsWe’vetalkedabouttheGETandPOSTHTTPverbsalready.Ifyou’renotfamiliarwithHTTPverbs,theothertwomostcommononesarePUTandDELETE,butthere’salsoHEAD,OPTIONS,PATCH,andtwoothersthatareprettymuchneverusedinnormalwebdevelopment,TRACEandCONNECT.

Here’sthequickrundown:GETrequestsaresourceandHEADasksforaheaders-onlyversionoftheGET,POSTcreatesaresource,PUToverwritesaresourceandPATCHmodifiesaresource,DELETEdeletesaresource,andOPTIONSaskstheserverwhichverbsareallowedatthisURL.

Page 105: Laravel: Up and Running: A Framework for Building Modern PHP

HTTPVerbsinLaravelAswe’veshownalready,youcandefinewhichverbsaroutewillmatchintheroutedefinitionusingRoute::get(),Route::post(),Route::any(),orRoute::match().YoucanalsomatchwithRoute::patch(),Route::put(),andRoute::delete().

ButhowdoesonesendarequestotherthanGETwithawebbrowser?First,themethodattributeinanHTMLformdeterminesitsHTTPverb:ifyourformhasamethodof"GET",itwillsubmitviaqueryparametersandaGETmethod;iftheformhasamethodof"POST",itwillsubmitviathepostbodyandaPOSTmethod.

JavaScriptframeworksmakeiteasytosendotherrequests,likeDELETEandPATCH.ButifyoufindyourselfneedingtosubmitHTMLformsinLaravelwithverbsotherthanGETorPOST,you’llneedtouseformmethodspoofing,whichisspoofingtheHTTPmethodinanHTMLform.

Page 106: Laravel: Up and Running: A Framework for Building Modern PHP

HTTPMethodSpoofinginHTMLFormsToinformLaravelthattheformyou’recurrentlysubmittingshouldbetreatedassomethingotherthanPOST,addahiddenvariablenamed_methodwiththevalueofeither"PUT","PATCH",or"DELETE",andLaravelwillmatchandroutethatformsubmissionasifitwereactuallyarequestwiththatverb.

TheforminExample3-31,sinceit’spassingLaravelthemethodof"DELETE",willmatchroutesdefinedwithRoute::delete()butnotthosewithRoute::post().

Example3-31.Formmethodspoofing<formaction="/tasks/5"method="POST">

<inputtype="hidden"name="_method"value="DELETE">

</form>

Page 107: Laravel: Up and Running: A Framework for Building Modern PHP

CSRFProtectionIfyou’vetriedtocreateandsubmitaforminaLaravelapplicationalready—includingtheforminExample3-31—you’velikelyrunintothedreadedTokenMismatchException.

Bydefault,allroutesinLaravelexcept“read-only”routes(thoseusingGET,HEAD,orOPTIONS)areprotectedagainstcross-siterequestforgery(CSRF)attacksbyrequiringatoken,intheformofaninputnamed_token,tobepassedalongwitheachrequest.Thistokenisgeneratedatthestartofeverysession,andeverynon–read-onlyroutecomparesthesubmitted_tokenagainstthesessiontoken.

Page 108: Laravel: Up and Running: A Framework for Building Modern PHP
Page 109: Laravel: Up and Running: A Framework for Building Modern PHP

WHATISCSRF?Across-siterequestforgeryiswhenonewebsitepretendstobeanother.Thegoalisforsomeonetohijackyourusers’accesstoyourwebsite,bysubmittingformsfromtheirwebsitetoyourwebsiteviathelogged-inuser’sbrowser.

ThebestwayaroundCSRFattacksistoprotectallinboundroutes—POST,DELETE,etc.—withatoken,whichLaraveldoesoutofthebox.

Youhavetwooptionsforgettingaroundthis.Thefirst,andpreferred,methodistoaddthe_tokeninputtoeachofyoursubmissions.InHTMLforms,that’ssimple;lookatExample3-32.

Example3-32.CSRFtokens<formaction="/tasks/5"method="POST">

<?phpechocsrf_field();?>

<!--or:-->

<inputtype="hidden"name="_token"value="<?phpechocsrf_token();?>">

</form>

InJavaScriptapplications,it’sabitmorework,butnotmuch.ThemostcommonsolutionforsitesusingJavaScriptframeworksistostorethetokenoneverypageina<meta>taglikethisone:

<metaname="csrf-token"content="<?phpechocsrf_token();?>"id="token">

Storingthetokenina<meta>tagmakesiteasytobindittothecorrectHTTPheader,whichyoucandooncegloballyforallrequestsfromyourJavaScriptframework,likeinExample3-33.

Example3-33.GloballybindingaheaderforCSRF//injQuery:

$.ajaxSetup({

headers:{

'X-CSRF-TOKEN':$('meta[name="csrf-token"]').attr('content')

}

});

//inVue:

Vue.http.interceptors.push((request,next)=>{

request.headers['X-CSRF-TOKEN']=

document.querySelector('#token').getAttribute('content');

next();

});

LaravelwillchecktheX-CSRF-TOKENoneveryrequest,andvalidtokenspassedtherewillmarktheCSRFprotectionassatisfied.

NotethattheVuesyntaxforCSRFinthisexampleisnotnecessaryifyou’reworkingwiththe5.3Vuebootstrap;italreadydoesthisworkforyou.

Page 110: Laravel: Up and Running: A Framework for Building Modern PHP

RedirectsSofartheonlythingswe’vereturnedfromacontrollermethodorroutedefinitionhavebeenviews.Butthereareafewotherstructureswecanreturntogivethebrowserinstructionsonhowtobehave.

First,let’scovertheredirect.Therearetwocommonwaystogeneratearedirect;we’llusetheredirectglobalhelperhere,butyoumaypreferthefacade.BothcreateaninstanceofIlluminate\Http\RedirectResponse,performsomeconveniencemethodsonit,andthenreturnit.Youcanalsodothismanually,butyou’llhavetodoalittlemoreworkyourself.TakealookatExample3-34toseeafewwaysyoucanreturnaredirect.

Example3-34.Differentwaystoreturnaredirect//Usingtheglobalhelpertogeneratearedirectresponse

Route::get('redirect-with-helper',function(){

returnredirect()->to('login');

});

//Usingtheglobalhelpershortcut

Route::get('redirect-with-helper-shortcut',function(){

returnredirect('login');

});

//Usingthefacadetogeneratearedirectresponse

Route::get('redirect-with-facade',function(){

returnRedirect::to('login');

});

Notethattheredirect()helperexposesthesamemethodsastheRedirectfacade,butitalsohasashortcut;ifyoupassparametersdirectlytothehelper,insteadofchainingmethodsafterit,it’sashortcuttotheto()redirectmethod.

Page 111: Laravel: Up and Running: A Framework for Building Modern PHP

redirect()->to()Themethodsignaturefortheto()methodforredirectslookslikethis:

functionto($to=null,$status=302,$headers=[],$secure=null)

$toisavalidinternalpath;$statusistheHTTPstatus(defaultingto302FOUND);$headersallowsyoutodefinewhichHTTPheaderstosendalongwithyourredirect;and$secureallowsyoutooverridethedefaultchoiceofhttpversushttps(whichisnormallysetbasedonyourcurrentrequestURL).Example3-35showsanotherexampleofitsuse.

Example3-35.redirect()->to()Route::get('redirect',function(){

returnredirect()->to('home');

//orsame,usingtheshortcut:

returnredirect('home');

});

Page 112: Laravel: Up and Running: A Framework for Building Modern PHP

redirect()->route()Theroute()methodisthesameastheto()method,butratherthanpointingtoaparticularpath,itpointstoaparticularroutename(seeExample3-36).

Example3-36.redirect()->route()Route::get('redirect',function(){

returnredirect()->route('conferences.index');

});

Notethat,sincesomeroutenamesrequireparameters,itsparameterorderisalittledifferent.route()hasanoptionalsecondparameterfortherouteparameters:

functionroute($to=null,$parameters=[],$status=302,$headers=[])

So,usingitmightlookalittlelikeExample3-37.

Example3-37.redirect()->route()withparametersRoute::get('redirect',function(){

returnredirect()->route('conferences.show',['conference'=>99]);

});

Page 113: Laravel: Up and Running: A Framework for Building Modern PHP

redirect()->back()Becauseofsomeofthebuilt-inconveniencesofLaravel’ssessionimplementation,yourapplicationwillalwayshaveknowledgeofwhattheuser ’spreviouslyvisitedpagewas.Thatopensuptheopportunityforaredirect()->()redirect,whichsimplyredirectstheusertowhateverpageshecamefrom.There’salsoaglobalshortcutforthis:back().

Page 114: Laravel: Up and Running: A Framework for Building Modern PHP

OtherRedirectMethodsTheredirectserviceprovidesothermethodsthatarelesscommonlyused,butstillavailable:

home()redirectstoaroutenamedhome.

refresh()redirectstothesamepagetheuseriscurrentlyon.

away()allowsforredirectingtoanexternalURLwithoutthedefaultURLvalidation.

secure()isliketo()withthesecureparametersetto"true".

action()allowsyoutolinktoacontrollerandmethodlikethis:redirect()->action('MyController@myMethod').

guest()isusedinternallybytheauthsystem(discussedinChapter9);whenauservisitsaroutehe’snotauthenticatedfor,thiscapturesthe“intended”routeandthenredirectstheuser(usuallytoaloginpage).

intended()isalsousedinternallybytheauthsystem;afterasuccessfulauthentication,thisgrabsthe“intended”URLstoredbytheguest()methodandredirectstheuserthere.

Page 115: Laravel: Up and Running: A Framework for Building Modern PHP

redirect()->with()Whenyou’reredirectinguserstodifferentpages,youoftenwanttopasscertaindataalongwiththem.Youcouldmanuallyflashthedatatothesession,butLaravelhassomeconveniencemethodstohelpyouwiththat.

Mostcommonly,youcanpassalongeitheranarrayofkeysandvaluesorasinglekeyandvalueusingwith(),likeinExample3-38.

Example3-38.RedirectwithdataRoute::get('redirect-with-key-value',function(){

returnredirect('dashboard')

->with('error',true);

});

Route::get('redirect-with-array',function(){

returnredirect('dashboard')

->with(['error'=>true,'message'=>'Whoops!']);

});

Page 116: Laravel: Up and Running: A Framework for Building Modern PHP
Page 117: Laravel: Up and Running: A Framework for Building Modern PHP

CHAININGMETHODSONREDIRECTSAswithmanyotherfacades,mostcallstotheRedirectfacadecanacceptfluentmethodchains,likethewith()callsinExample3-38.Learnmoreaboutfluencyin“WhatIsaFluentInterface?”.

YoucanalsousewithInput(),asinExample3-39,toredirectwiththeuser ’sforminputflashed;thisismostcommoninthecaseofavalidationerror,whereyouwanttosendtheuserbacktotheformshejustcamefrom.

Example3-39.RedirectwithforminputRoute::get('form',function(){

returnview('form');

});

Route::post('form',function(){

returnredirect('form')

->withInput()

->with(['error'=>true,'message'=>'Whoops!']);

});

TheeasiestwaytogettheflashedinputthatwaspassedwithwithInput()isusingtheold()helper,whichcanbeusedtogetalloldinput(old())orjustthevalueforaparticularkey(old('username'),withthesecondparameterasthedefaultifthereisnooldvalue).You’llcommonlyseethisinviews,whichallowsthisHTMLtobeusedbothonthe“create”andthe“edit”viewforthisform:

<inputname="username"value="<?=

old('username','Defaultusernameinstructionshere');

?>">

Speakingofvalidation,thereisalsoausefulmethodforpassingerrorsalongwitharedirectresponse:withErrors().Youcanpassitany“provider”oferrors,whichmaybeanerrorstring,anarrayoferrors,or,mostcommonly,aninstanceoftheIlluminateValidator,whichwe’llcoverinChapter10.Example3-40showsanexampleofitsuse.

Example3-40.RedirectwitherrorsRoute::post('form',function(){

$validator=Validator::make($request->all()),$this->validationRules);

if($validator->fails()){

returnredirect('form')

->withErrors($validator)

->withInput();

}

});

withErrors()automaticallysharesan$errorsvariablewiththeviewsofthepageit’sredirectingto,foryoutohandlehoweveryou’dlike.

Page 118: Laravel: Up and Running: A Framework for Building Modern PHP

THEVALIDATE()SHORTCUTINCONTROLLERMETHODSLikehowExample3-40looks?Ifyou’redefiningyourroutesinacontroller,there’sasimpleandpowerfultoolthatcleansupthatcode.Readmorein“validate()intheControllerUsingValidatesRequests”.

Page 119: Laravel: Up and Running: A Framework for Building Modern PHP

AbortingtheRequestAsidefromreturningviewsandredirects,themostcommonwaytoexitarouteistoabort.Thereareafewgloballyavailablemethods(abort(),abort_if(),andabort_unless()),whichoptionallytakeHTTPstatuscodes,amessage,andaheadersarrayasparameters.

AsExample3-41shows,abort_if()andabort_unless()takeafirstparameterthatisevaluatedforitstruthiness,andperformtheabortdependingontheresult.

Example3-41.403ForbiddenabortsRoute::post('something-you-cant-do',function(Illuminate\Http\Request){

abort(403,'Youcannotdothat!');

abort_unless($request->has('magicToken'),403);

abort_if($request->user()->isBanned,403);

});

Page 120: Laravel: Up and Running: A Framework for Building Modern PHP

CustomResponsesThereareafewotheroptionsavailableforustoreturn,solet’sgooverthemostcommonresponsesafterviews,redirects,andaborts.Justlikewithredirects,youcaneitherusetheresponse()helperortheResponsefacadetorunthesemethodson.

Page 121: Laravel: Up and Running: A Framework for Building Modern PHP

response()->make()IfyouwanttocreateanHTTPresponsemanually,justpassyourdataintothefirstparameterofresponse()->make():e.g.,returnresponse()->make('Hello,World!').Onceagain,thesecondparameteristheHTTPstatuscodeandthethirdisyourheaders.

Page 122: Laravel: Up and Running: A Framework for Building Modern PHP

response()->json()and->jsonp()TocreateaJSON-encodedHTTPresponsemanually,passyourJSON-ablecontent(arrays,collections,orwhateverelse)tothejson()method:e.g.,returnresponse()->json(User::all());.It’sjustlikemake(),exceptitjson_encodesyourcontentandsetstheappropriateheaders.

Page 123: Laravel: Up and Running: A Framework for Building Modern PHP

response()->download()and->file()Tosendafilefortheendusertodownload,passeitheranSplFileInfoinstanceorastringfilenametodownload(),withanoptionalsecondparameterofthefilename:e.g.,returnresponse()->download('file501751.pdf','myFile.pdf').

Todisplaythesamefileinthebrowser(ifit’saPDForanimageorsomethingelsethebrowsercanhandle),useresponse()->file()instead,whichtakesthesameparameters.

Page 124: Laravel: Up and Running: A Framework for Building Modern PHP

TestingInsomeothercommunities,theideaofunittestingcontrollermethodsiscommon,butwithinLaravel(andmostofthePHPcommunity),it’smostcommontorelyonapplicationtestingtotestthefunctionalityofroutes.

Forexample,toverifythataPOSTrouteworkscorrectly,wecanwriteatestlikeExample3-42.

Example3-42.WritingasimplePOSTroutetest//AssignmentTest.php

publicfunctiontest_post_creates_new_assignment()

{

$this->post('/assignments',[

'title'=>'Mygreatassignment'

]);

$this->seeInDatabase('assignments',[

'title'=>'Mygreatassignment'

]);

}

Didwedirectlycallthecontrollermethods?No.Butweensuredthatthegoalofthisroute—toreceiveaPOSTandsaveitsimportantinformationtothedatabase—wasmet.

Youcanalsousesimilarsyntaxtovisitarouteandverifythatcertaintextshowsuponthepage,orthatclickingcertainbuttonsdoescertainthings(seeExample3-43).

Example3-43.WritingasimpleGETroutetest//AssignmentTest.php

publicfunctiontest_list_page_shows_all_assignments()

{

$assignment=Assignment::create([

'title'=>'Mygreatassignment'

]);

$this->visit('assignments')

->dee(['Mygreatassignment']);

}

Page 125: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRLaravel’sroutesaredefinedinroutes/web.phpandroutes/api.php,whereyoucandefinetheexpectedpathforeachroute,whichsegmentsarestaticandwhichareparameters,whichHTTPverbscanaccesstheroute,andhowtoresolveit.Youcanalsoattachmiddlewaretoroutes,groupthem,andgivethemnames.

WhatisreturnedfromtherouteclosureorcontrollermethoddictateshowLaravelrespondstotheuser.Ifit’sastringoraview,it’spresentedtotheuser;ifit’sothersortsofdata,it’sconvertedtoJSONandpresentedtotheuser;andifit’saredirect,itforcesaredirect.

Laravelprovidesaseriesoftoolsandconveniencestosimplifycommonrouting-relatedtasksandstructures.Theseincluderesourcecontrollers,routemodelbinding,andformmethodspoofing.

Page 126: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter4.BladeTemplating

Comparedtomostotherbackendlanguages,PHPactuallyfunctionsrelativelywellasatemplatinglanguage.Butithasitsshortcomings,andit’salsojustuglytobeusing<?phpinlineallovertheplace,soyoucanexpectmostmodernframeworkstoofferatemplatinglanguage.

LaraveloffersacustomtemplatingenginecalledBlade,whichisinspiredby.NET’sRazorengine.Itboastsaconcisesyntax,ashallowlearningcurve,apowerfulandintuitiveinheritancemodel,andeasyextensibility.

ForaquicklookatwhatwritingBladelookslike,checkoutExample4-1.

Example4-1.Bladesamples<h1>{{$group->title}}</h1>

{!!$group->heroImageHtml()!!}

@forelse($usersas$user)

•{{$user->first_name}}{{$user->last_name}}<br>

@empty

Nousersinthisgroup.

@endforelse

Asyoucansee,Bladeintroducesaconventioninwhichitscustomtags,called“directives,”[email protected]’llusedirectivesforallofyourcontrolstructuresandalsoforinheritanceandanycustomfunctionalityyouwanttoadd.

Blade’ssyntaxiscleanandconcise,soatitscoreit’sjustmorepleasantandtidytoworkwiththanthealternatives.Butthemomentyouneedanythingofanycomplexityinyourtemplates—nestedinheritance,complexconditionals,orrecursion—Bladestartstoreallyshine.JustlikethebestLaravelcomponents,ittakescomplexapplicationrequirementsandmakesthemeasyandaccessible.

Additionally,sinceallBladesyntaxiscompiledintonormalPHPcodeandthencached,it’sfastanditallowsyoutousenativePHPinyourBladefilesifyouwant.However,I’drecommmendavoidingusageofPHPifatallpossible—usuallyifyouneedtodoanythingthatyoucan’tdowithBladeoracustomBladedirective,itdoesn’tbelonginthetemplate.

Page 127: Laravel: Up and Running: A Framework for Building Modern PHP
Page 128: Laravel: Up and Running: A Framework for Building Modern PHP

USINGTWIGWITHLARAVELUnlikemanyotherSymfony-basedframeworks,Laraveldoesn’tuseTwigbydefault.Butifyou’rejustinlovewithTwig,there’saTwigBridgepackagethatmakesiteasytouseTwiginsteadofBlade.

Page 129: Laravel: Up and Running: A Framework for Building Modern PHP

EchoingDataAsyoucanseeinExample4-1,{{and}}areusedtowrapsectionsofPHPthatyou’dliketoecho.{{$variable}}issimilarto<?=$variable?>inplainPHP.

It’sdifferentinoneway,however,andyoumight’veguessedthisalready:BladeescapesallechoesbydefaultusingPHP’shtmlentities()toprotectyourusersfrommaliciousscriptinsertion.Thatmeans{{$variable}}isfunctionallyequivalentto<?=htmlentities($variable)?>.Ifyouwanttoechowithouttheescaping,use{!!and!!}instead.

{{AND}}WHENUSINGAFRONTENDTEMPLATINGFRAMEWORK

Youmight’venoticedthattheechosyntaxforBlade({{}})issimilartotheechosyntaxformanyfrontendframeworks.So,howdoesLaravelknowwhenyou’rewritingBladeversusHandlebars?

Bladewillignoreany{{that’[email protected],itwillparsethefirstofthefollowingexamples,butthesecondwillbeechoedoutdirectly:

//ParsedasBlade;thevalueof$bladeVariableisechoedtotheview

{{$bladeVariable}}

//@isremoved,and"{{handlebarsVariable}}"echoedtotheviewdirectly

@{{handlebarsVariable}}

Page 130: Laravel: Up and Running: A Framework for Building Modern PHP

ControlStructuresMostofthecontrolstructuresinBladewillbeveryfamiliar.ManydirectlyechothenameandstructureofthesametaginPHP.

Thereareafewconveniencehelpers,butingeneral,thecontrolstructuresjustlookcleanerthantheywouldinPHP.

Page 131: Laravel: Up and Running: A Framework for Building Modern PHP

ConditionalsFirst,let’stakealookatthecontrolstructuresthatallowforlogic.

@ifBlade’s@if($condition)compilesto<?phpif($condition):?>.@else,@elseif,and@endifalsocompiletotheexactsamesyntaxinPHP.TakealookatExample4-2forsomeexamples.

Example4-2.@if,@else,@elseif,and@endif@if(count($talks)===1)

Thereisonetalkatthistimeperiod.

@elseif(count($talks)===0)

Therearenotalksatthistimeperiod.

@else

Thereare{{count($talks)}}talksatthistimeperiod.

@endif

JustlikewiththenativePHPconditionals,youcanmixandmatchthesehowyouwant.Theydon’thaveanyspeciallogic;there’sliterallyaparserlookingforsomethingwiththeshapeof@if($condition)andreplacingitwiththeappropriatePHPcode.

@unlessand@endunless@unless,ontheotherhand,isanewsyntaxthatdoesn’thaveadirectequivalentinPHP.It’sthedirectinverseof@if.@unless($condition)isthesameas<?phpif(!$condition).SeeitinuseinExample4-3.

Example4-3.@unlessand@endunless@unless($user->hasPaid())

Youcancompleteyourpaymentbyswitchingtothepaymenttab.

@endunless

Page 132: Laravel: Up and Running: A Framework for Building Modern PHP

LoopsNext,let’stakealookattheloops.

@for,@foreach,and@while@for,@foreach,and@whileworkthesameinBladeastheydoinPHP;seeExamples4-4,4-5,and4-6.

Example4-4.@forand@endfor@for($i=0;$i<$talk->slotsCount();$i++)

Thenumberis{{$i}}<br>

@endfor

Example4-5.@foreachand@endforeach@foreach($talksas$talk)

•{{$talk->title}}({{$talk->length}}minutes)<br>

@endforeach

Example4-6.@whileand@endwhile@while($item=array_pop($items))

{{$item->orSomething()}}<br>

@endwhile

@forelse@forelseisa@foreachthatalsoallowsyoutoprograminafallbackiftheobjectyou’reiteratingoverisempty.Wesawitinactionatthestartofthischapter;Example4-7showsanotherexample.

Example4-7.@forelse@forelse($talksas$talk)

•{{$talk->title}}({{$talk->length}}minutes)<br>

@empty

Notalksthisday.

@endforelse

Page 133: Laravel: Up and Running: A Framework for Building Modern PHP

$LOOPWITHIN@FOREACHAND@FORELSEThe@[email protected]’snotavailableinPHPforeachloops:the$loopvariable.Usedwithina@foreachor@forelseloop,thisvariablewillreturnastdClassobjectwiththefollowingproperties:

index

The0-basedindexofthecurrentitemintheloop;0wouldmean“firstitem”

iteration

The1-basedindexofthecurrentitemintheloop;1wouldmean“firstitem”

remaining

Howmanyitemsremainintheloop;ifthecurrentitemisthefirstofthree,thiswillbe2

count

Thecountofitemsintheloop

first

Abooleanindicatingwhetherthisisthefirstitemintheloop

last

Abooleanindicatingwhetherthisisthelastitemintheloop

depth

Howmany“levels”deepthisloopis:1foraloop,2foraloopwithinaloop,etc.

parent

Areferencetothe$loopvariablefortheparentloopitem;ifthisloopiswithinanother@foreachloopotherwise,null

Here’sanexampleofhowtouseit:

<ul>

@foreach($pagesas$page)

<li>{{$loop->iteration}}:{{$page->title}}

@if($page->hasChildren())

<ul>

@foreach($page->children()as$child)

<li>{{$loop->parent->iteration}}.

{{$loop->iteration}}:

{{$child->title}}</li>

@endforeach

</ul>

@endif

</li>

@endforeach

</ul>

Page 134: Laravel: Up and Running: A Framework for Building Modern PHP

orIfyou’reeverunsurewhetheravariableisset,you’reprobablyusedtocheckingisset()onitbeforeechoingit,andechoingsomethingelseifit’snotset.Bladehasaconveniencehelper,or,thatdoesthisforyouandletsyousetadefaultfallback:{{$titleor"Default"}}willechothevalueof$titleifit’sset,or“Default”ifnot.

Page 135: Laravel: Up and Running: A Framework for Building Modern PHP

TemplateInheritanceBladeprovidesastructurefortemplateinheritancethatallowsviewstoextend,modify,andincludeotherviews.

Here’showinheritanceisstructuredwithBlade.

Page 136: Laravel: Up and Running: A Framework for Building Modern PHP

DefiningSectionswith@section/@showand@yieldLet’sstartwithatop-levelBladelayout,likeinExample4-8.Thisisthedefinitionofthegenericpagewrapperthatwe’lllaterplacepage-specificcontentinto.

Example4-8.Bladelayout<!--resources/views/layouts/master.blade.php-->

<html>

<head>

<title>MySite|@yield('title','HomePage')</title>

</head>

<body>

<divclass="container">

@yield('content')

</div>

@section('footerScripts')

<scriptsrc="app.js"></script>

@show

</body>

</html>

ThislooksabitlikeanormalHTMLpage,butyoucanseewe’veyieldedintwoplaces(titleandcontent),andwe’vedefinedasectioninathird(footerScripts).

WehavethreeBladedirectivesherethateachlookalittledifferent:@yield('title','HomePage')alone,@yield('content')withadefineddefault,and@section...@showwithactualcontentinit.

Allthreefunctionessentiallythesame.Allthreearedefiningthatthere’sasectionwithagivenname(whichisthefirstparameter).Allthreearedefiningthatthesectioncanbeextendedlater.Andallthreearedefiningwhattodoifthesectionisn’textended,eitherbyprovidingastringfallback('HomePage'),nofallback(whichwilljustnotshowanythingifit’snotextended),oranentireblockfallback(inthiscase,<scriptsrc="app.js"></script>).

What’sdifferent?Well,clearly,@yield('content')hasnodefaultcontent.Butadditionally,thedefaultcontentin@yield('title')onlywillbeshownifit’sneverextended.Ifitisextended,itschildsectionswillnothaveprogrammaticaccesstothedefaultvalue.@section...@show,ontheotherhand,isbothdefiningadefaultanddoingsoinawaythatitsdefaultcontentswillbeavailabletoitschildren,through@parent.

Onceyouhaveaparentlayoutlikethis,youcanextenditlikeinExample4-9.

Example4-9.ExtendingaBladelayout<!--resources/views/dashboard.blade.php-->

@extends('layouts.master')

@section('title','Dashboard')

@section('content')

Welcometoyourapplicationdashboard!

@endsection

@section('footerScripts')

@parent

Page 137: Laravel: Up and Running: A Framework for Building Modern PHP

<scriptsrc="dashboard.js"></script>

@endsection

Page 138: Laravel: Up and Running: A Framework for Building Modern PHP

@SHOWVERSUS@ENDSECTIONYoumayhavenoticedthatExample4-8uses@section...@show,butExample4-9uses@[email protected]’sthedifference?

Use@showwhenyou’redefiningtheplaceforasection,intheparenttemplate.Use@endsectionwhenyou’redefiningthecontentforatemplateinachildtemplate.

ThischildviewwillactuallyallowustocoverafewnewconceptsinBladeinheritance.

@extendsFirst,with@extends('layouts.master'),wedefinethatthisviewshouldnotberenderedonitsown,butthatitinsteadextendsanotherview.Thatmeansitsroleistodefinethecontentofvarioussections,butnottostandalone.It’salmostmorelikeaseriesofbucketsofcontent,ratherthananHTMLpage.Thislinealsodefinesthattheviewit’sextendinglivesatresources/views/layouts/master.blade.php.

Eachfileshouldonlyextendoneotherfile,andthe@extendscallshouldbethefirstlineofthefile.

@sectionand@endsectionSecond,with@section('title','Dashboard'),weprovideourcontentforthefirstsection,title.Sincethecontentissoshort,insteadofusing@sectionand@endsectionwe’rejustusingashortcut.Thisallowsustopassthecontentinasthesecondparameterof@sectionandthenmoveon.Ifit’sabitdisconcertingtosee@sectionwithout@endsection,youcouldjustusethenormalsyntax.

Third,with@section('content')andon,weusethenormalsyntaxtodefinethecontentsxofthecontentsection.We’lljustthrowalittlegreetinginfornow.Note,however,thatwhenyou’reusing@sectioninachildview,youenditwith@endsection(oritsalias@stop),insteadof@show,whichisreservedfordefiningsectionsinparentviews.

Page 139: Laravel: Up and Running: A Framework for Building Modern PHP

@parentFourth,with@section('footerScripts')andon,weusethenormalsyntaxtodefinethecontentsofthefooterScriptssection.

Butremember,weactuallydefinedthatcontent(or,atleast,its“default”)alreadyinthemasterlayout.Sothistime,wehavetwooptions:wecaneitheroverwritethecontentfromtheparentview,orwecanaddtoit.

Youcanseethatwehavetheoptiontoincludethecontentfromtheparentbyusingthe@parentdirectivewithinthesection.Ifwedidn’t,thecontentofthissectionwouldentirelyoverwriteanythingdefinedintheparentforthissection.

Page 140: Laravel: Up and Running: A Framework for Building Modern PHP

@includeNowthatwe’veestablishedthebasicsofinheritance,thereareafewmoretrickswecanperform.

Whatifwe’reinaviewandwanttopullinanotherview?Maybewehaveacall-to-action“Signup”buttonthatwewanttore-usearoundthesite.Andmaybewewanttocustomizeitsbuttontexteverytimeweuseit.TakealookatExample4-10.

Example4-10.Includingviewpartialswith@include<!--resources/views/home.blade.php-->

<divclass="content"data-page-name="{{$pageName}}">

<p>Here'swhyyoushouldsignupforourapp:<strong>It'sGreat.</strong></p>

@include('sign-up-button',['text'=>'Seejusthowgreatitis'])

</div>

<!--resources/views/sign-up-button.blade.php-->

<aclass="buttonbutton--callout"data-page-name="{{$pageName}}">

<iclass="exclamation-icon"></i>{{$text}}

</a>

@includepullsinthepartialand,optionally,passesdataintoit.Notethatnotonlycanyouexplicitlypassdatatoanincludeviathesecondparameterof@include,butyoucanalsoreferenceanyvariableswithintheincludedfilethatareavailabletotheincludingview($pageName,inthisexample).Onceagain,youcandowhateveryouwant,butIwouldrecommendyouconsideralwaysexplicitlypassingeveryvariablethatyouintendtouse,justforclarity.

Page 141: Laravel: Up and Running: A Framework for Building Modern PHP

@eachYoucanprobablyimaginesomecircumstancesinwhichyou’dneedtoloopoveranarrayorcollectionand@includeapartialforeachitem.There’sadirectiveforthat:@each.

Let’ssaywehaveasidebarcomposedofmodules,andwewanttoincludemultiplemodules,eachwithadifferenttitle.TakealookatExample4-11.

Example4-11.Usingviewpartialsinaloopwith@each<!--resources/views/sidebar.blade.php-->

<divclass="sidebar">

@each('partials.module',$modules,'module','partials.empty-module')

</div>

<!--resources/views/partials/module.blade.php-->

<divclass="sidebar-module">

<h1>{{$module->title}}</h1>

</div>

<!--resources/views/partials/empty-module.blade.php-->

<divclass="sidebar-module">

Nomodules:(

</div>

Considerthat@eachsyntax.Thefirstparameteristhenameoftheviewpartial.Thesecondisthearrayorcollectiontoiterateover.Thethirdisthevariablenamethateachitem(inthiscase,eachelementinthe$modulesarray)willbepassedtotheviewas.Andtheoptionalfourthparameteristheviewtoshowifthearrayorcollectionisempty(or,optionally,youcanpassastringinherethatwillbeusedasyourtemplate).

Page 142: Laravel: Up and Running: A Framework for Building Modern PHP

ViewComposersandServiceInjectionAswecoveredinChapter3,it’ssimpletopassdatatoourviewsfromtheroutedefinition(seeExample4-12).

Example4-12.ReminderonhowtopassdatatoviewsRoute::get('passing-data-to-views',function(){

returnview('dashboard')

->with('key','value');

});

Therearetimes,however,whenyouwillfindyourselfpassingthesamedataoverandovertomultipleviews.Or,youmightfindyourselfusingaheaderpartialorsomethingsimilarthatrequiressomedata;willyounowhavetopassthatdatainfromeveryroutedefinitionthatmighteverloadthatheaderpartial?

Page 143: Laravel: Up and Running: A Framework for Building Modern PHP

BindingDatatoViewsUsingViewComposersThankfully,there’sasimplerway.Thesolutioniscalledaviewcomposer,anditallowsyoutodefinethatanytimeaparticularviewloads,itshouldhavecertaindatapassedtoit—withouttheroutedefinitionhavingtopassthatdatainexplicitly.

Let’ssayyouhaveasidebaroneverypage,whichisdefinedinapartialnamedpartials.sidebar(resources/views/partials/sidebar.blade.php)andthenincludedoneverypage.Thissidebarshowsalistofthelastsevenpoststhatwerepublishedonyoursite.Ifit’soneverypage,everyroutedefinitionwouldnormallyhavetograbthatlistandpassitin,likeinExample4-13.

Example4-13.PassingsidebardatainfromeveryrouteRoute::get('home',function(){

returnview('home')

->with('posts',Post::recent());

});

Route::get('about',function(){

returnview('about')

->with('posts',Post::recent());

});

Thatcouldgetannoyingquickly.Instead,we’regoingtouseviewcomposersto“share”thatvariablewithaprescribedsetofviews.Wecandothisafewways,solet’sstartsimpleandmoveup.

SharingavariablegloballyFirst,thesimplestoption:justglobally“share”avariablewitheveryviewinyourapplicationlikeinExample4-14.

Example4-14.Sharingavariableglobally//Someserviceprovider

publicfunctionboot()

{

...

view()->share('posts',Post::recent());

}

Ifyouwanttouseview()->share(),thebestplacewouldbetheboot()methodofaserviceprovidersothatthebindingrunsoneverypageload.YoucancreateacustomViewComposerServiceProvider(seeChapter11formoreaboutserviceproviders),butfornowjustputitinApp\Providers\AppServiceProviderintheboot()method.

Usingview()->share()makesthevariableaccessibletoeveryviewintheentireapplication,however,soitmightbeoverkill.

Closure-basedviewcomposersThenextoptionistouseaclosure-basedviewcomposertosharevariableswithasingleview,likeinExample4-15.

Page 144: Laravel: Up and Running: A Framework for Building Modern PHP

Example4-15.Creatingaclosure-basedviewcomposerview()->composer('partials.sidebar',function($view){

$view->with('posts',Post::recent());

});

Asyoucansee,we’vedefinedthenameoftheviewwewantitsharedwithinthefirstparameter(partials.sidebar)andthenpassedaclosuretothesecondparameter;intheclosure,we’veused$view->with()toshareavariable,butnowonlywithaspecificview.

Page 145: Laravel: Up and Running: A Framework for Building Modern PHP

VIEWCOMPOSERSFORMULTIPLEVIEWSAnywhereaviewcomposerisbindingtoaparticularview(likeinExample4-15,whichbindstopartials.sidebar),youcanpassanarrayofviewnamesinsteadtobindtomultipleviews.

Youcanalsouseanasteriskintheviewpath,asinpartials.*,tasks.*,orjust*:

view()->composer(

['partials.header','partials.footer'],

function(){

$view->with('posts',Post::recent());

}

);

view()->composer('partials.*',function(){

$view->with('posts',Post::recent());

});

Class-basedviewcomposersFinally,themostflexiblebutalsomostcomplexoptionistocreateadedicatedclassforyourviewcomposer.

First,let’screatetheviewcomposerclass.There’snoformallydefinedplaceforviewcomposerstolive,butthedocsrecommendApp\Http\ViewComposers.So,let’screateApp\Http\ViewComposers\RecentPostsComposerlikeinExample4-16.

Example4-16.Aviewcomposer<?php

namespaceApp\Http\ViewComposers;

useApp\Post;

useIlluminate\Contracts\View\View;

classRecentPostsComposer

{

private$posts;

publicfunction__construct(Post$posts)

{

$this->posts=$posts;

}

publicfunctioncompose(View$view)

{

$view->with('posts',$this->posts->recent());

}

}

Asyoucansee,we’reinjectingthePostmodel(typehintedconstructorparametersofviewcomposerswillbeautomaticallyinjected;seeChapter11formoreonthecontaineranddependencyinjection).Notethatwecouldskiptheprivate$postsandtheconstructorinjectionandjustusePost::recent()inthecompose()methodifwewanted.Then,whenthiscomposeriscalled,itrunsthecompose()method,inwhichwebindthepostsvariabletotheresultofrunningtherecent()method.

Page 146: Laravel: Up and Running: A Framework for Building Modern PHP

Liketheothermethodsofsharingvariables,thisviewcomposerneedstohaveabindingsomewhere.Again,you’dlikelycreateacustomViewComposerServiceProvider,butfornow,asseeninExample4-17,we’lljustputitintheboot()methodofApp\Providers\AppServiceProvider.

Example4-17.RegisteringaviewcomposerinAppServiceProvider//AppServiceProvider

publicfunctionboot()

{

...

view()->composer(

'partials.sidebar',

\App\Http\ViewComposers\RecentPostsComposer::class

);

}

Notethatthisbindingisthesameasaclosure-basedviewcomposer,butinsteadofpassingaclosure,we’repassingtheclassnameofourviewcomposer.Now,everytimeBladerendersthepartials.sidebarview,it’llautomaticallyrunourproviderandpasstheviewapostsvariablesettotheresultsoftherecent()methodonourPostmodel.

Page 147: Laravel: Up and Running: A Framework for Building Modern PHP

BladeServiceInjectionTherearethreeprimarytypesofdatawe’remostlikelytoinjectintoaview:collectionsofdatatoiterateover,singleobjectsthatwe’redisplayingonthepage,andservicesthatgeneratedataorviews.

Withaservice,thepatternwillmostlikelylooklikeExample4-18,whereweinjectaninstanceofouranalyticsserviceintotheroutedefinitionbytypehintingitintheroute’smethodsignature,andthenpassitintotheview.

Example4-18.InjectingservicesintoaviewviatheroutedefinitionconstructorRoute::get('backend/sales',function(AnalyticsService$analytics){

returnview('backend.sales-graphs')

->with('analytics',$analytics);

});

Justaswithviewcomposers,Blade’sserviceinjectionoffersaconvenientshortcuttoreduceduplicationinyourroutedefinitions.Normally,thecontentofaviewusingouranalyticsservicemightlooklikeExample4-19.

Example4-19.Usinganinjectednavigationserviceinaview<divclass="finances-display">

{{$analytics->getBalance()}}/{{$analytics->getBudget()}}

</div>

Bladeserviceinjectionmakesiteasytoinjectaninstanceofaclassoutsideofthecontainerdirectlyfromtheview,likeinExample4-20.

Example4-20.Injectingaservicedirectlyintoaview@inject('analytics','App\Services\Analytics')

<divclass="finances-display">

{{$analytics->getBalance()}}/{{$analytics->getBudget()}}

</div>

Asyoucansee,this@injectdirectivehasactuallymadean$analyticsvariableavailable,whichwe’reusinglaterinourview.

Thefirstparameterof@injectisthenameofthevariableyou’reinjecting,andthesecondparameteristheclassorinterfacethatyouwanttoinjectaninstanceof.ThisisresolvedjustlikewhenyoutypehintadependencyinaconstructorelsewhereinLaravel;ifyou’reunfamiliarwithhowthatworks,gotoChapter11tolearnmore.

Justlikeviewcomposers,Bladeserviceinjectionmakesiteasytomakecertaindataorfunctionalityavailabletoeveryinstanceofaview,withouthavingtoinjectitviatheroutedefinitioneverytime.

Page 148: Laravel: Up and Running: A Framework for Building Modern PHP

CustomBladeDirectivesAllofthebuilt-insyntaxofBladethatwe’vecoveredsofar—@if,@unless,andsoon—arecalleddirectives.EachBladedirectiveisamappingbetweenapattern(e.g.,@if($condition))andaPHPoutput(e.g.,<?phpif($condition):?>).

Directivesaren’tjustforthecore;youcanactuallycreateyourown.Youmightthinkdirectivesaregoodformakinglittleshortcutstobiggerpiecesofcode—forexample,using@button('buttonName')andhavingitexpandtoalargersetofbuttonHTML.Thisisn’taterribleidea,butforsimplecodeexpansionlikethisyoumightbebetteroffincludingaviewpartial.

I’vefoundcustomdirectivestobethemostusefulwhentheysimplifysomeformofrepeatedlogic.Saywe’retiredofhavingtowrapourcodewith@if(auth()->guest())(tocheckifauserisloggedinornot)[email protected],itmightbeworthhavingacustomserviceprovidertoregisterthese,butfornowlet’sjustputitintheboot()methodofApp\Providers\AppServiceProvider.TakealookatExample4-21toseewhatthisbindingwilllooklike.

Example4-21.BindingacustomBladedirective//AppServiceProvider

publicfunctionboot()

{

Blade::directive('ifGuest',function(){

return"<?phpif(auth()->guest()):?>";

});

}

We’venowregisteredacustomdirective,@ifGuest,whichwillbereplacedwiththePHPcode<?phpif(auth()->guest()):?>.

Thismightfeelstrange.You’rewritingastringthatwillbereturnedandthenexecutedasPHP.Butwhatthismeansisthatyoucannowtakethecomplex,orugly,orunclear,orrepetitiveaspectsofyourPHPtemplatingcodeandhidethembehindclear,simple,andexpressivesyntax.

Page 149: Laravel: Up and Running: A Framework for Building Modern PHP

CUSTOMDIRECTIVERESULTCACHINGYoumightbetemptedtodosomelogictomakeyourcustomdirectivefasterbyperforminganoperationinthebindingandthenembeddingtheresultwithinthereturnedstring:

Blade::directive('ifGuest',function(){

//Antipattern!Donotcopy.

$ifGuest=auth()->guest();

return"<?phpif({$ifGuest}):?>";

});

Theproblemwiththisideaisthatitassumesthisdirectivewillbere-createdoneverypageload.However,Bladecachesaggressively,soyou’regoingtofindyourselfinabadspotifyoutrythis.

Page 150: Laravel: Up and Running: A Framework for Building Modern PHP

ParametersinCustomBladeDirectivesWhatifyouwanttocheckaconditioninyourcustomlogic?CheckoutExample4-22.

Example4-22.CreatingaBladedirectivewithparameters//Binding

Blade::directive('newlinesToBr',function($expression){

return"<?phpechonl2br({$expression});?>";

});

//Inuse

<p>@newlinesToBr($message->body)</p>

The$expressionparameterreceivedbytheclosurerepresentswhatever ’swithintheparentheses.Asyoucansee,wethengenerateavalidPHPcodesnippetandreturnit.

Page 151: Laravel: Up and Running: A Framework for Building Modern PHP
Page 152: Laravel: Up and Running: A Framework for Building Modern PHP

$EXPRESSIONPARAMETERSCOPINGBEFORELARAVEL5.3BeforeLaravel5.3,the$expressionparameteralsoincludedtheparenthesesthemselves.So,inExample4-22,$expression(whichis$message->bodyinLaravel5.3andlater)wouldhaveinsteadbeen($message->body),andwewould’vehadtowrite<?phpechonl2br{$expression};?>.

Ifyoufindyourselfconstantlywritingthesameconditionallogicoverandover,youshouldconsideraBladedirective.

Page 153: Laravel: Up and Running: A Framework for Building Modern PHP

Example:UsingCustomBladeDirectivesforaMultitenantAppSo,let’simaginewe’rebuildinganapplicationthatsupportsmultitenancy,whichmeansusersmightbevisitingthesitefromwww.myapp.com,client1.myapp.com,client2.myapp.com,orelsewhere.

SupposewehavewrittenaclasstoencapsulatesomeofourmultitenancylogicandnameditContext.Thisclasswillcaptureinformationandlogicaboutthecontextofthecurrentvisit,suchaswhotheauthenticateduserisandwhethertheuserisvisitingthepublicwebsiteoraclientsubdomain.

We’llprobablyfrequentlyresolvethatContextclassinourviewsandperformconditionalsonit,likeinExample4-23.Theapp('context')isashortcuttogetaninstanceofaclassfromthecontainer,whichwe’lllearnmoreaboutinChapter11.

Example4-23.ConditionalsoncontextwithoutacustomBladedirective@if(app('context')->isPublic())

&copy;CopyrightMyAppLLC

@else

&copy;Copyright{{app('context')->client->name}}

@endif

Whatifwecouldsimplify@if(app('context')->isPublic())tojust@ifPublic?Let’sdoit.CheckoutExample4-24.

Example4-24.ConditionalsoncontextwithacustomBladedirective//Binding

Blade::directive('ifPublic',function(){

return"<?phpif(app('context')->isPublic()):?>";

});

//Inuse

@ifPublic

&copy;CopyrightMyAppLLC

@else

&copy;Copyright{{app('context')->client->name}}

@endif

Sincethisresolvestoasimpleifstatement,wecanstillrelyonthenative@[email protected],wecouldalsocreateacustom@elseIfClientdirective,oraseparate@ifClientdirective,orreallywhateverelsewewant.

Page 154: Laravel: Up and Running: A Framework for Building Modern PHP

TestingThemostcommonmethodoftestingviewsisthroughapplicationtesting,meaningthatyou’reactuallycallingtheroutethatdisplaystheviewsandensuringtheviewshavecertaincontent(seeExample4-25).Youcanalsoclickbuttonsorsubmitformsandensurethatyouareredirectedtoacertainpage,orthatyouseeacertainerror.(You’lllearnmoreabouttestinginChapter12.)

Example4-25.Testingthataviewdisplayscertaincontent//EventsTest.php

publicfunctiontest_list_page_shows_all_events()

{

$event1=factory(Event::class)->create();

$event2=factory(Event::class)->create();

$this->visit('events')

->see($event1->title)

->see($event2->title);

}

Youcanalsotestthatacertainviewhasbeenpassedaparticularsetofdata,which,ifitaccomplishesyourtestinggoals,islessfragilethancheckingforcertaintextonthepage.Example4-26demonstratesthisapproach.

Example4-26.Testingthataviewwaspassedcertaincontent//EventsTest.php

publicfunctiontest_list_page_shows_all_events()

{

$event1=factory(Event::class)->create();

$event2=factory(Event::class)->create();

$this->visit('events');

$this->assertViewHas('events',Event::all());

$this->assertViewHasAll([

'events'=>Event::all(),

'title'=>'EventsPage'

]);

$this->assertViewMissing('dogs');

}

In5.3,wegainedtheabilitytopassaclosureto$assertViewHas(),meaningwecancustomizehowwewanttocheckmorecomplexdatastructures.Example4-27illustrateshowwemightusethis.

Example4-27.PassingaclosuretoassertViewHas()//EventsTest.php

publicfunctiontest_list_page_shows_all_events()

{

$event1=factory(Event::class)->create();

$this->visit('events/'.$event1->id);

$this->assertViewHas('event',function($event)use($event1){

return$event->id===$event1->id;

});

}

Page 155: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRBladeisLaravel’stemplatingengine.Itsprimaryfocusisaclear,concise,andexpressivesyntaxwithpowerfulinheritanceandextensibility.Its“safeecho”bracketsare{{and}},itsunprotectedechobracketsare{!!and!!},andithasaseriesofcustomtagscalleddirectivesthatallbeginwith@(@ifand@unless,forexample).

Youcandefineaparenttemplateandleave“holes”initforcontentusing@yieldand@section/@show.Youcanthenteachitschildviewstoextenditusing@extends('parent.view.name'),anddefinetheirsectionsusing@section/@endsection.Youuse@parenttoreferencethecontentoftheblock’sparent.

Viewcomposersmakeiteasytodefinethat,everytimeaparticularvieworsubviewloads,itshouldhavecertaininformationavailabletoit.Andserviceinjectionallowstheviewitselftorequestdatastraightfromtheapplicationcontainer.

Page 156: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter5.FrontendComponents

LaravelisprimarilyaPHPframework,butitalsohasaseriesofcomponentsfocusedongeneratingfrontendcode.Someofthese,likepaginationandmessagebags,arePHPhelpersthattargetthefrontend,butLaravelalsoprovidesaGulp-basedbuildsystemcalledElixirandsomeconventionsaroundnon-PHPassets.

SinceElixirisatthecoreofthenon-PHPfrontendcomponents,let’sstartthere.

Page 157: Laravel: Up and Running: A Framework for Building Modern PHP

ElixirElixir(nottobeconfusedwiththefunctionalprogramminglanguage)isabuildtoolthatprovidesasimpleuserinterfaceandaseriesofconventionsontopofGulp.Elixir ’scorefeatureissimplifyingthemostcommonGulptasksbymeansofacleanerAPIandaseriesofnamingandapplicationstructureconventions.

AQUICKINTRODUCTIONTOGULP

GulpisaJavaScripttooldesignedforcompilingstaticassetsandcoordinatingotherstepsofyourbuildprocess.

GulpissimilartoGrunt,Rake,ormake—itallowsyoutodefineanaction(calleda“task”inGulp)orseriesofactionstotakeeverytimeyoubuildyourapplication.ThiswillcommonlyincluderunningaCSSpreprocessorlikeSassorLESS,copyingfiles,concatenatingandminifyingJavaScript,andmuchmore.

Gulp,andthereforeElixir,isbasedontheideaofstreams.Mosttaskswillbeginbyloadingsomefilesintothestreambuffer,andthenthetaskwillapplytransformationstothecontent—preprocessit,minifyit,andthenmaybesavethecontenttoanewfile.

Atitscore,ElixirisjustatoolinyourGulptoolbox.Thereisn’tevensuchathingasanElixirfile;you’lldefineyourElixirtasksinyourgulpfile.js.ButtheylookalotdifferentfromvanillaGulptasks,andyou’llhavetodoalotlessworktogetthemrunningoutofthebox.

Let’slookatacommonexample:runningSasstopreprocessyourCSSstyles.InanormalGulpenvironment,thatmightlookalittlebitlikeExample5-1.

Example5-1.CompilingaSassfileinGulpvargulp=require('gulp'),

sass=require('gulp-ruby-sass'),

autoprefixer=require('gulp-autoprefixer'),

rename=require('gulp-rename'),

notify=require('gulp-notify'),

livereload=require('gulp-livereload'),

lr=require('tiny-lr'),

server=lr();

gulp.task('sass',function(){

returngulp.src('resources/assets/sass/app.scss')

.pipe(sass({

style:'compressed',

sourcemap:true

}))

.pipe(autoprefixer('last2version','ie9','ios6'))

.pipe(gulp.dest('public/css'))

.pipe(rename({suffix:'.min'}))

.pipe(livereload(server))

.pipe(notify({

title:"Karani",

message:"Stylestaskcomplete."

}));

});

Now,I’veseenworse.Itreadswell,andit’sclearwhat’sgoingon.Butthere’salothappeningthatyou’lljustpullintoeverysiteyouevermake.Itcangetconfusingandrepetitive.

Let’strythatsametaskinElixir(Example5-2).

Example5-2.CompilingaSassfileinElixir

Page 158: Laravel: Up and Running: A Framework for Building Modern PHP

varelixir=require('laravel-elixir');

elixir(function(mix){

mix.sass('app.scss');

});

That’sit.Thatcoversallthebasics—preprocessing,notification,folderstructure,autoprefixing,andmuchmore.

Page 159: Laravel: Up and Running: A Framework for Building Modern PHP

ES6INELIXIR6Elixir6,whichcameoutwithLaravel5.3,changedalotofthesyntaxtouseES6,thelatestversionofJavaScript.Here’swhatExample5-2lookslikeinElixir6:

constelixir=require('laravel-elixir');

elixir(mix=>{

mix.sass('app.scss')

});

Don’tworry;thisdoesexactlythesamething.

Page 160: Laravel: Up and Running: A Framework for Building Modern PHP

ElixirFolderStructureMuchofElixir ’ssimplicitycomesfromtheassumeddirectorystructure.Whymakethedecisionfreshineverynewapplicationaboutwherethesourceandcompiledassetslive?JuststickwithElixir ’sconvention,andyouwon’thavetothinkaboutiteveragain.

EverynewLaravelappcomeswitharesourcesfolderwithanassetssubfolder,whichiswhereElixirwillexpectyourfrontendassetstolive.YourSasswillliveinresources/assets/sass,oryourLESSinresources/assets/less,andyourJavaScriptwillliveinresources/assets/js.Thesewillexporttopublic/cssandpublic/js.

Butifyou’reinterestedinchangingthestructure,youcanalwayschangethesourceandpublicpathsbychangingtheappropriateproperties(assetsPathandpublicPath)ontheelixir.configobject.

Page 161: Laravel: Up and Running: A Framework for Building Modern PHP

RunningElixirSinceElixirrunsonGulp,you’llneedtosetupafewtoolsbeforeusingit:

1. First,you’llneedNode.jsinstalled.VisittheNodewebsitetolearnhowtogetitrunning.

2. Next,you’llneedtoinstallGulpgloballyonyourmachine.Justrunnpminstall--globalgulp-clifromtheterminalanywhereonyourmachine.OnceNodeandGulpareinstalled,youwillneverhavetorunthosecommandsagain.Nowyou’rereadytoinstallthisproject’sdependencies.

3. Opentheprojectrootinyourterminal,andrunnpminstalltoinstalltherequiredpackages(LaravelshipswithanElixir-readypackage.jsonfiletodirectNPM).

You’renowsetup!YoucanrungulptorunGulp/Elixironce,gulpwatchtolistenforrelevantfilechangesandruninresponse,orgulpscriptsorgulpstylestojustrunthescriptorstyletasks.

Page 162: Laravel: Up and Running: A Framework for Building Modern PHP

WhatDoesElixirProvide?We’vealreadycoveredthatElixircanpreprocessyourCSSusingSassorLESS.Itcanconcatenatefiles,minifythem,renamethem,andcopythem,anditcancopyentiredirectoriesorindividualfiles.

ElixircanalsoprocessES6/ES2015JavaScriptandrunWebpack,Rollup,and/orAutoprefixeronyourcode.Notonlythat,butmostofthemoderncodingstandardsforJavaScriptandCSSarecoveredoneveryscriptorstyle,outofthebox.

Elixircanalsorunyourtests.There’samethodforPHPUnitandoneforPHPSpec;bothlistentochangestoyourtestfilesandrerunyourtestsuiteeverytimeyoumakeanychanges.

TheElixirdocumentationcoversalloftheseoptionsandmore,butwe’llcoverafewspecificusecasesinthefollowingsections.

The--productionflagBydefault,Elixirdoesn’tminifyallthefilesit’sgenerating.Butifyouwanttorunthebuildscriptsin“production”mode,withallminificationenabled,youcanjustaddthe--productionflag:

$gulp--production

PassingmultiplefilesMostoftheElixirmethodsthatnormallyacceptasinglefile(e.g.,mix.sass('app.scss'))canalsotakeanarrayoffiles,likeinExample5-3.

Example5-3.CompilingmultiplefileswithElixirconstelixir=require('laravel-elixir');

elixir(mix=>{

mix.sass([

'app.scss',

'public.scss'

]);

});

SourcemapsBydefault,Elixirgeneratessourcemapsforyourfiles—you’llseethemasa.{filename}.mapfilenexttoeachgeneratedfile.

Ifyou’renotfamiliarwithsourcemaps,theyworkwithanysortofpreprocessortoteachyourbrowser ’swebinspectorwhichfilesgeneratedthecompiledsourceyou’reinspecting.

Withoutsourcemaps,ifyouuseyourbrowser ’sdevelopmenttoolstoinspectaparticularCSSruleorJavaScriptaction,you’lljustseeabigmessofcompiledcode.Withsourcemaps,yourbrowsercanpinpointtheexactlineofthesourcefile,whetheritbeSassorJavaScriptorwhateverelse,thatgeneratedtheruleyou’reinspecting.

Page 163: Laravel: Up and Running: A Framework for Building Modern PHP

Ifyoudon’twantsourcemaps,youcanalwayschangetheconfigurationbeforeyourelixirblocklikeinExample5-4.

Example5-4.DisablingsourcemapsinElixirconstelixir=require('laravel-elixir');

elixir.config.sourcemaps=false;

elixir(mix=>{

mix.sass('app.scss');

});

PreprocessorlessCSSIfyoudon’twanttodealwithapreprocessor,there’sacommandforthat—itwillgraballofyourCSSfiles,concatenatethem,andoutputthemtothepublic/cssdirectory,justasiftheyhadbeenrunthroughapreprocessor.Ifyoudon’tspecifyanouputfilename,it’llendupinall.css.Thereareafewoptions,whichyoucanseeinExample5-5.

Example5-5.CombiningstylesheetswithElixirconstelixir=require('laravel-elixir');

elixir(mix=>{

//Combinesallfilesfromresources/assets/cssandsubfolders

mix.styles();

//Combinesfilesfromresources/assets/css

mix.styles([

'normalize.css',

'app.css'

]);

//Combinesallstylesfromotherdirectory

mix.stylesIn('resources/some/other/css/directory');

//Combinesgivenstylesfromresources/assets/css

//andoutputstoacustomdirectory

mix.styles([

'normalize.css',

'app.css'

],'public/other/css/output.css');

//Combinesgivenstylesfromcustomdirectory

//andoutputstoacustomdirectory

mix.styles([

'normalize.css',

'app.css'

],'public/other/css/output.css','resources/some/other/css/directory');

});

ConcatenatingJavaScriptTheoptionsavailableforworkingwithnormalJavaScriptfilesareverysimilartothoseavailablefornormalCSSfiles.TakealookatExample5-6.Likewithstyles(),anycommandsnotprovidedwithanoutputfilenamewilloutputtopublic/js/all.js.

Example5-6.CombiningJavaScriptfileswithElixirconstelixir=require('laravel-elixir');

elixir(mix=>{

//Combinesfilesfromresources/assets/js

mix.scripts([

Page 164: Laravel: Up and Running: A Framework for Building Modern PHP

'jquery.js',

'app.js'

]);

//Combinesallscriptsfromotherdirectory

mix.scriptsIn('resources/some/other/js/directory');

//Combinesgivenscriptsfromresources/assets/js

//andoutputstoacustomdirectory

mix.scripts([

'jquery.js',

'app.js'

],'public/other/js/output.js');

//Combinesgivenscriptsfromcustomdirectory

//andoutputstoacustomdirectory

mix.scripts([

'jquery.js',

'app.js'

],'public/other/js/output.js','resources/some/other/js/directory');

});

ProcessingJavaScriptIfyouwanttoprocessyourJavaScript—forexample,tocompileyourES6codeintoplainJavaScript—ElixirmakesiteasytouseeitherWebpackorRollupforthispurpose(seeExample5-7).

Example5-7.ProcessingJavaScriptfilesinElixirwithWebpackorRollupelixir(function(mix){

mix.webpack('app.js');

//or

mix.rollup('app.js');

});

Thesescriptslookfortheprovidedfilenameinresources/assets/jsandoutputtopublic/js/all.js.

YoucanusemorecomplicatedaspectsofWebpack’sfeaturesetbycreatingawebpack.config.jsfileinyourprojectroot.

Page 165: Laravel: Up and Running: A Framework for Building Modern PHP
Page 166: Laravel: Up and Running: A Framework for Building Modern PHP

COMPILINGJAVASCRIPTINELIXIR5PriortoLaravel5.3/Elixir6,you’llwanttocompileyourJavaScriptusingmix.browserify('app.js').

VersioningMostofthetipsfromSteveSouders’EvenFasterWebSites(O’Reilly)havemadetheirwayintooureverydaydevelopmentpractices.Wemovescriptstothefooter,reducethenumberofHTTPrequests,andmore,oftenwithoutevenrealizingwherethoseideasoriginated.

OneofSteve’stipsisstillveryrarelyimplemented,though,andthatissettingaverylongcachelifeonassets(scripts,styles,andimages).Doingthismeanstherewillbefewerrequeststoyourservertogetthelatestversionofyourassets.Butitalsomeansthatusersareextremelylikelytohaveacachedversionofyourassets,whichwillmakethingsgetoutdated,andthereforebreak,quickly.

Thesolutiontothisisversioning.Appendauniquehashtoeachasset’sfilenameeverytimeyourunyourbuildscript,andthenthatuniquefilewillbecachedindefinitely—oratleastuntilthenextbuild.

What’stheproblem?Well,firstyouneedtogettheuniquehashesgeneratedandappendedtoyourfilenames.Butyoualsowillneedtoupdateyourviewsoneverybuildtoreferencethenewfilenames.

Asyoucanprobablyguess,Elixirhandlesthatforyou,andit’sincrediblysimple.Therearetwocomponents:theversioningtaskinElixir,andtheelixir()PHPhelper.First,youcanversionyourassetsbyrunningmix.version()likeinExample5-8.

Example5-8.mix.versionconstelixir=require('laravel-elixir');

elixir(mix=>{

mix.version('public/css/all.css');

});

Thiswillgenerateaversionofthespecifiedfilewithauniquehashappendedtoitinthepublic/builddirectory—somethinglikepublic/build/css/all-84fa1258.css.

Next,usethePHPelixir()helperinyourviewstorefertothatfilelikeinExample5-9.

Example5-9.Usingtheelixir()helperinviews<linkrel="stylesheet"href="{{elixir("css/all.css")}}">

//willoutputsomethinglike:

<linkrel="stylesheet"href="/build/css/all-84fa1258.css">

Page 167: Laravel: Up and Running: A Framework for Building Modern PHP

HOWDOESELIXIR VERSIONINGWORKBEHINDTHESCENES?

Elixirusesgulp-rev,whichtakescareofappendingthehashestothefilenames,andalsogeneratesafilenamedpublic/build/rev-manifest.json.Thisstorestheinformationtheelixir()helperneedstofindthegeneratedfile.Here’swhatasamplerev-manifest.jsonlookslike:

{

"css/all.css":"css/all-7f592e49.css"

}

TestsWithElixirit’seasytorunyourPHPUnitorPHPSpectestseverytimeyourtestfileschange.

Youhavetwooptions,mix.phpUnit()andmix.phpSpec(),andeachwillruntherespectiveframeworksdirectlyfromthevendorfolder,soyouwon’thavetodoanythingtomakethemwork.

IfyouaddoneofthesemethodstoyourGulpfile,however,you’llfindtheyonlyrunonce,evenifyou’reusinggulpwatch.Howdoyougetthemtorespondtochangesinyourtestsfolder?

There’saseparateGulpcommandforthat:gulptdd.ThisgrabsjustthetestcommandsoutofyourGulpfile,whetherphpUnit()orphpSpec(),listenstotheappropriatefolder,andrerunsthetestsuitewheneveranyfileschange.

ElixirextensionsElixirdoesn’tjustprovideasimplesyntaxforitsownprebuilttasks;italsomakesiteasytodefineyourown.

Let’ssayyouwanttosavetexttoalogfileatcertainpoints.That’sashellcommand,whichisecho"message">>file.log.Normallywe’ddefinethisasaGulptask,usingshell('echo"message">>file.log'),likeinExample5-10.

Example5-10.UsingaGulptaskinElixir//Definethetask

gulp.task("log",function(){

varmessage="Somethinghappened";

gulp.src("").pipe(shell('echo"'+message+'">>file.log'));

});

elixir(mix=>{

//UsethetaskinElixir

mix.task('log');

//Bindthetasktoruneverytimecertainfilesarechanged

mix.task('log','resources/somefiles/to/watch/**/*')

});

However,ifwewantalittlemorecontrol—forexample,ifwewanttobeabletoactuallypassinthemessage,whichisreallysortofvitaltomakethisparticulartaskwork—wecan

Page 168: Laravel: Up and Running: A Framework for Building Modern PHP

createanElixirextensionlikeinExample5-11.

Example5-11.CreatinganElixirextension//Eitheringulpfile.js,orinanexternalfileandrequiredingulpfile.js

vargulp=require("gulp"),

shell=require("gulp-shell"),

elixir=require("laravel-elixir");

elixir.extend("log",function(message){

newTask('log',function(){

returngulp.src('').pipe(shell('echo"'+message+'">>file.log'));

})

.watch('./resources/some/files/**/*');

});

Aswithanycomponent,wehaven’tcoveredeverythingthereistolearnaboutElixir,buthopefullyyou’velearnedenoughtogetyourunningwithit.Wanttolearnmore?Checkoutthedocs.

Page 169: Laravel: Up and Running: A Framework for Building Modern PHP

PaginationForsomethingthatissocommonacrosswebapplications,paginationstillcanbewildlycomplicatedtoimplement.Thankfully,Laravelhasabuilt-inconceptofpagination,andit’salsohookedintoEloquentresultsandtherouterbydefault.

ABRIEFINTRODUCTIONTOELOQUENT

We’llbecoveringEloquent,databaseaccess,andLaravel’squerybuilderindepthinChapter8,buttherewillbeafewreferencesbetweennowandthenthatwillmakeabasicunderstandinguseful.

EloquentisLaravel’sActiveRecorddatabaseobject-relationalmapper(ORM),whichmakesiteasytorelateaPostclass(model)tothepostsdatabasetable,andgetallrecordswithacalllikePost::all().

ThequerybuilderisthetoolthatmakesitpossibletomakecallslikePost::where('active',true)->get()orevenDB::table('users')->all().You’rebuildingaquerybychainingmethodsoneafteranother.

Page 170: Laravel: Up and Running: A Framework for Building Modern PHP

PaginatingDatabaseResultsThemostcommonplaceyou’llseepaginationiswhenyouaredisplayingtheresultsofadatabasequeryandtherearetoomanyresultsforasinglepage.Eloquentandthequerybuilderbothreadthepagequeryparameterfromthecurrentpagerequestanduseittoprovideapaginate()methodonanyresultsets;thesingleparameteryoushouldpasspaginate()ishowmanyresultsyouwantperpage.TakealookatExample5-12toseehowthisworks.

Example5-12.Paginatingaquerybuilderresponse//PostsController

publicfunctionindex()

{

returnview('posts.index',['posts'=>DB::table('posts')->paginate(20)]);

}

Example5-12definesthatthisrouteshouldreturn20postsperpage,andwilldefinewhich“page”ofresultsthecurrentuserisonbasedontheURL’spagequeryparameter,ifithasone.Eloquentmodelsallhavethesamepaginate()method.

Whenyoudisplaytheresultsinyourview,yourcollectionwillnowhavealinks()methodonit(orrender()forLaravel5.1)thatwilloutputthepaginationcontrols,withbootstrapclassnamesassignedtothembydefault(seeExample5-13).

Example5-13.Renderingpaginationlinksinatemplate//posts/index.blade.php

<table>

@foreach($postsas$post)

<tr><td>{{$post->title}}</td></tr>

@endforeach

</table>

{{$posts->links()}}

//Bydefaut,$posts->links()willoutputsomethinglikethis:

<ulclass="pagination">

<liclass="disabled"><span>&laquo;</span></li>

<liclass="active"><span>1</span></li>

<li><ahref="http://myapp.com/posts?page=2">2</a></li>

<li><ahref="http://myapp.com/posts?page=3">3</a></li>

<li><ahref="http://myapp.com/posts?page=2"rel="next">&raquo;</a></li>

</ul>

Page 171: Laravel: Up and Running: A Framework for Building Modern PHP

ManuallyCreatingPaginatorsIfyou’renotworkingwithEloquentorthequerybuilder,orifyou’reworkingwithacomplexquery(e.g.,thoseusinggroupBy),youmightfindyourselfneedingtocreateapaginatormanually.Thankfully,youcandothatwiththeIlluminate\Pagination\PaginatororIlluminate\Pagination\LengthAwarePaginatorclasses.

ThedifferencebetweenthetwoclassesisthatPaginatorwillonlyprovidepreviousandnextbuttons,butnolinkstoeachpage;LengthAwarePaginatorneedstoknowthelengthofthefullresult,sothatitcangeneratelinksforeachindividualpage.YoumayfindyourselfwantingtousethePaginatoronlargeresultsets,soyourpaginatordoesn’thavetobeawareofamassivecountofresultsthatmightbecostlytorun.

BoththePaginatorandtheLengthAwarePaginatorrequireyoutomanuallyextractthesubsetofcontentthatyouwanttopasstotheview.TakealookatExample5-14foranexample.

Example5-14.ManuallycreatingapaginatorinLaravel5.2and5.3useIlluminate\Http\Request;

useIlluminate\Pagination\Paginator;

Route::get('people',function(Request$request){

$people=[...];//hugelistofpeople

$perPage=15;

$offsetPages=$request->input('page',1)-1;

//ThePaginatorwillnotsliceyourarrayforyou

$people=array_slice(

$people,

$offsetPages*$perPage,

$perPage

);

returnnewPaginator(

$people,

$perPage

);

});

ThePaginatorsyntaxhaschangedoverthelastfewversionsofLaravel,soifyou’reusing5.1,takealookatthedocstofindthecorrectsyntax.

Page 172: Laravel: Up and Running: A Framework for Building Modern PHP

MessageBagsAnothercommonbutpainfulfeatureinwebapplicationsispassingmessagesbetweenvariouscomponentsoftheapp,whentheendgoalistosharethemwiththeuser.Yourcontroller,forexample,mightwanttosendavalidationmessage:“Theemailfieldmustbeavalidemailaddress.”However,thatparticularmessagedoesn’tjustneedtomakeittotheviewlayer;itactuallyneedstosurvivearedirectandthenendupintheviewlayerofadifferentpage.Howdowestructurethismessaginglogic?

Illuminate\Support\MessageBagisaclasstaskedwithstoring,categorizing,andreturningmessagesthatareintendedfortheenduser.Itgroupsallmessagesbykey,wherethekeysarelikelytobesomethinglikeerrorsandmessages,andprovidesconveniencemethodsforgettingallitsstoredmessagesoronlythoseforaparticularkey,andforoutputtingthesemessagesinvariousformats.

YoucanspinupanewinstanceofMessageBagmanuallylikeinExample5-15.

Example5-15.ManuallycreatingandusingMessageBag$messages=[

'errors'=>[

'Somethingwentwrongwithedit1!'

],

'messages'=>[

'Edit2wassuccessful.'

]

];

$messagebag=new\Illuminate\Support\MessageBag($messages);

//Checkforerrors;ifthereareany,decorateandecho

if($messagebag->has('errors')){

echo'<ulid="errors">';

foreach($messagebag->get('errors','<li><b>:message</b></li>')as$error){

echo$error;

}

echo'</ul>';

}

MessagebagsarealsocloselyconnectedtoLaravel’svalidators(learnmorein“Validation”):whenvalidatorsreturnerrors,theyactuallyreturnaninstanceofMessageBag,whichyoucanthenpasstoyourvieworattachtoaredirectusingredirect('route')->withErrors($messagebag).

LaravelpassesanemptyinstanceofMessageBagtoeveryview,assignedtothevariable$errors,andifyou’veflashedamessagebagusingwithErrors()onaredirect,itwillgetassignedtothat$errorsvariableinstead.Thatmeanseveryviewcanalwaysassumeithasan$errorsMessageBagitcancheckinwhateverplaceitdoesitsvalidation,whichleadstoExample5-16asacommonsnippetdevelopersplaceoneverypage.

Example5-16.Errorbagsnippet//partials/errors.blade.php

@if($errors->any())

<divclass="alertalert-danger">

<ul>

Page 173: Laravel: Up and Running: A Framework for Building Modern PHP

@foreach($errorsas$error)

<li>{{$error}}</li>

@endforeach

</ul>

</div>

@endif

Page 174: Laravel: Up and Running: A Framework for Building Modern PHP
Page 175: Laravel: Up and Running: A Framework for Building Modern PHP

MISSING$ERRORSVARIABLEIfyouhaveanyroutesthataren’tunderthewebmiddlewaregroup,theywon’thavethesessionmiddleware,whichmeanstheywon’thavethis$errorsvariableavailable.

Page 176: Laravel: Up and Running: A Framework for Building Modern PHP

NamedErrorBagsSometimesyouneedtodifferentiatemessagebagsnotjustbykey(noticesversuserrors)butalsobycomponent.Maybeyouhavealoginformandasignupformonthesamepage;howdoyoudifferentiatethem?

WhenyousenderrorsalongwitharedirectusingwithErrors(),thesecondparameteristhenameofthebag:redirect('dashboard')->withErrors($validator,'login').Then,onthedashboard,youcanuse$errors->logintocallallofthemethodswesawbefore:any(),count(),andmore.

Page 177: Laravel: Up and Running: A Framework for Building Modern PHP

StringHelpers,Pluralization,andLocalizationAsdevelopers,wetendtolookatblocksoftextasbigplaceholderdivs,waitingfortheclienttoputrealcontentintothem.Seldomareweinvolvedinanylogicinsidetheseblocks.

Butthereareafewcircumstanceswhereyou’llbegratefulforthetoolsLaravelprovidesforstringmanipulation.

Page 178: Laravel: Up and Running: A Framework for Building Modern PHP

TheStringHelpersandPluralizationLaravelhasaseriesofhelpersformanipulatingstrings.They’reavailableasmethodsontheStrclass(e.g.,Str::plural()),butmostalsohaveaglobalhelperfunction(e.g.,str_plural()).

TheLaraveldocumentationcoversallofthestringhelpersindetail,buthereareafewofthemostcommonlyusedhelpers:

e

Ashortcutforhtml_entities

starts_with,ends_with,str_containsCheckastring(firstparameter)toseeifitstartswith,endswith,orcontainsanotherstring(secondparameter)

str_is

Checkswhetherastring(secondparameter)matchesaparticularpattern(firstparameter)—forexample,foo*willmatchfoobarandfoobaz

str_slug

ConvertsastringtoaURL-typeslugwithhyphens

str_plural(word,num),str_singularPluralizesawordorsingularizesit;English-only(e.g.,str_plural('dog')returnsdogs)

Page 179: Laravel: Up and Running: A Framework for Building Modern PHP

LocalizationLocalizationallowsyoutodefinemultiplelanguagesandmarkanystringsastargetsfortranslation.Youcansetafallbacklanguage,andevenhandlepluralizationvariations.

InLaravel,you’llneedtosetanapplicationlocaleatsomepointduringthepageloadsothelocalizationhelpersknowwhichbucketoftranslationstopullfrom.You’lldothiswithApp::setLocale($localeName),andyou’lllikelyputitinaserviceprovider.Fornowyoucanjustputitintheboot()methodofAppServiceProvider,butyoumaywanttocreateaLocaleServiceProviderifyouendupwithmorethanjustthisonelocale-relatedbinding.

SETTINGTHELOCALEFOR EACHREQUEST

ItcanbeconfusingatfirsttoworkouthowLaravel“knows”theuser’slocale,orprovidestranslations.Mostofthatworkisonyouasthedeveloper.Let’slookatalikelyscenario.

You’llprobablyhavesomefunctionalityallowingtheusertochoosealocale,orpossiblyattemptingtoautomaticallydetectit.Eitherway,yourapplicationwilldeterminethelocale,andthenyou’llstorethatinaURLparameterorasessioncookie.Thenyourserviceprovider—somethinglikeaLocaleServiceProvider,maybe—willgrabthatkeyandsetitasapartofLaravel’sbootstrap.

Somaybeyouruserisathttp://myapp.com/es/contacts.YourLocaleServiceProviderwillgrabthatesstring,andthenrunApp::setLocale('es').Goingforward,everytimeyouaskforatranslationofastring,LaravelwilllookfortheSpanishversionofthatstring,whichyouwillneedtohavedefinedsomewhere.

Youcandefineyourfallbacklocaleinconfig/app.php,whereyoushouldfindafallback_localekey.Thisallowsyoutodefineadefaultlanguageforyourapplication,whichLaravelwilluseifitcan’tfindatranslationfortherequestedlocale.

BasiclocalizationSo,howdowecallforatranslatedstring?There’sahelperfunction,trans($key),thatwillpullthestringforthecurrentlocaleforthepassedkeyor,ifitdoesn’texist,grabitfromthedefaultlocale.Example5-17demonstrateshowabasictranslationworks.We’llusetheexampleofa“backtothedashboard”linkatthetopofadetailpage.

Example5-17.Basicuseoftrans()//NormalPHP

<?phpechotrans('navigation.back');?>

//Blade

{{trans('navigation.back')}}

//Bladedirective

@lang('navigation.back')

Let’sassumeweareusingtheeslocalerightnow.Laravelwilllookforafileinresources/lang/es/navigation.php,whichitwillexpecttoreturnanarray.It’lllookforabackkeyonthatarray,andifitexists,it’llreturnitsvalue.TakealookatExample5-18forasample.

Page 180: Laravel: Up and Running: A Framework for Building Modern PHP

Example5-18.Usingatranslation//resources/lang/es/navigation.php

return[

'back'=>'Volveralpanel'

];

//routes/web.php

Route::get('/es/contacts/show/:id',function(){

//Settingitmanually,forthisexample,insteadofinaserviceprovider

App::setLocale('es');

returnview('contacts.show');

});

//resources/views/contacts/show.blade.php

<ahref="/contacts">{{trans('navigation.back')}}</a>

ParametersinlocalizationTheprecedingexamplewasrelativelysimple.Let’sdigintosomethataremorecomplex.Whatifwewanttodefinewhichdashboardwe’rereturningto?TakealookatExample5-19.

Example5-19.Parametersintranslations//resources/lang/en/navigation.php

return[

'back'=>'Backto:sectiondashbaord'

];

//resources/views/contacts/show.blade.php

{{trans('navigation.back',['section'=>'contacts'])}}

Asyoucansee,prependingawordwithacolon(:section)marksitasaplaceholderthatcanbereplaced.Thesecond,optional,parameteroftrans()isanarrayofvaluestoreplacetheplaceholderswith.

PluralizationinlocalizationWealreadycoveredpluralization,sonowjustimagineyou’redefiningyourownpluralizationrules.Therearetwowaystodoit;we’llstartwiththesimplest,inExample5-20.

Example5-20.Definingasimpletranslationwithanoptionforpluralization//resources/lang/en/messages.php

return[

'task-deletion'=>'Youhavedeletedatask|Youhavesuccessfullydeletedtasks'

];

//resources/views/dashboard.blade.php

@if($numTasksDeleted>0)

{{trans_choice('messages.task-deletion',$numTasksDeleted)}}

@endif

Asyoucansee,wehaveatrans_choice()method,whichtakesthecountofitemsaffectedasitssecondparameter;andfromthisitwilldeterminewhichstringtouse.

YoucanalsouseanytranslationdefinitionsthatarecompatiblewithSymfony’smuchmorecomplexTranslationcomponent;seeExample5-21foranexample.

Example5-21.UsingtheSymfony’sTranslationcomponent//resources/lang/es/messages.php

return[

Page 181: Laravel: Up and Running: A Framework for Building Modern PHP

'task-deletion'=>"{0}Youdidn'tmanagetodeleteanytasks.|".

"[1,4]Youdeletedafewtasks.|".

"[5,Inf]Youdeletedawholetonoftasks."

];

Page 182: Laravel: Up and Running: A Framework for Building Modern PHP

TestingInthischapterwefocusedprimarilyonLaravel’sfrontendcomponents.Thesearelesslikelytheobjectsofunittests,buttheymayattimesbeusedinyourintegrationtests.

Page 183: Laravel: Up and Running: A Framework for Building Modern PHP

TestingwithElixirYou’renotgoingtobewritinganytestsaroundyourElixirtasks.However,Elixirprovidessomefunctionsthatwillhelpwithyourtesting,solet’stalkaboutthoseforasecond.

Ifyouaddmix.phpunit()ormix.phpspec()toyourgulpfile.js,everytimeyourungulpitwillrunyourtestsonce,inline,asapartofyourbuildscript.

Andeverytimeyourungulpwatch,Elixirwilllistentoanychangetoyourtestfilesoranyothercorefiles(likeroutes/web.php)andre-runPHPUnitorPHPSpeceverytimeyoumakeanychangestothosefiles.

Page 184: Laravel: Up and Running: A Framework for Building Modern PHP

TestingMessageandErrorBagsTherearetwoprimarywaysoftestingmessagespassedalongwithmessageanderrorbags.First,youcanperformabehaviorinyourapplicationteststhatsetsamessagethatwilleventuallybedisplayedsomewhere,thenredirecttothatpageandassertthattheappropriatemessageisshown.

Second,forerrors(whichisthemostcommonusecase),youcanassertthesessionhaserrorswith$this->assertSessionHasErrors($bindings=[]).TakealookatExample5-22toseewhatthismightlooklike.

Example5-22.Assertingthesessionhaserrorspublicfunctiontest_missing_email_field_errors()

{

$this->post('person/create',['name'=>'Japheth']);

$this->assertSessionHasErrors(['email']);

}

Page 185: Laravel: Up and Running: A Framework for Building Modern PHP

TranslationandLocalizationThesimplestwaytotestlocalizationiswithapplicationtests.Settheappropriatecontext(whetherbyURLorsession),visit()thepage,andassertthatyouseetheappropriatecontent.

Page 186: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRAsafull-stackframework,Laravelprovidestoolsandcomponentsforthefrontendaswellasthebackend.

ElixirisawrapperaroundcommonGulpbuildtasksthatmakesitsimpletousethemostmodernbuildsteps.ElixirmakesiteasytoaddCSSpreprocessors;JavaScripttranspilation,concatenation,andminification;andmuchmore.

Laravelalsooffersotherinternaltoolsthattargetthefrontend,includingpagination,messageanderrorbags,andlocalization.

Page 187: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter6.CollectingandHandlingUserData

WebsitesthatbenefitfromaframeworklikeLaraveloftendon’tjustservestaticcontent.Manydealwithcomplexandmixeddatasources,andoneofthemostcommon(andmostcomplex)ofthesesourcesisuserinputinitsmyriadforms:URLpaths,queryparameters,POSTdata,andfileuploads.

Laravelprovidesacollectionoftoolsforgathering,validating,normalizing,andfilteringuser-provideddata.We’lllookatthosehere.

Page 188: Laravel: Up and Running: A Framework for Building Modern PHP

InjectingaRequestObjectThemostcommontoolforaccessinguserdatainLaravelisinjectinganinstanceoftheIlluminate\Http\Requestobject.Itprovideseasyaccesstoallofthewaysuserscanprovideinputtoyoursite:POST,postedJSON,GET(queryparameters),andURLsegments.

Page 189: Laravel: Up and Running: A Framework for Building Modern PHP
Page 190: Laravel: Up and Running: A Framework for Building Modern PHP

OTHEROPTIONSFORACCESSINGREQUESTDATAThere’salsoarequest()globalhelperandaRequestfacade,bothofwhichexposethesamemethods.EachoftheseoptionsexposestheentireIlluminateRequestobject,butfornowwe’reonlygoingtocoverthemethodsthatspecificallyrelatetouserdata.

Sincewe’replanningoninjectingaRequestobject,let’stakeaquicklookathowtogetthe$requestobjectwe’llbecallingallthesemethodson:

Route::post('form',function(Illuminate\Http\Request$request){

//$request->etc()

});

Page 191: Laravel: Up and Running: A Framework for Building Modern PHP

$request->all()Justlikethenamesuggests,$request->all()givesyouanarraycontainingalloftheinputtheuserhasprovided,fromeverysource.Let’ssay,forsomereason,youdecidedtohaveaformPOSTtoaURLwithaqueryparameter—e.g.,sendingaPOSTtohttp://myapp.com/post?utm=12345.TakealookatExample6-1toseewhatyou’dgetfrom$request->all().(Notethat$request->all()alsocontainsinformationaboutanyfilesthatwereuploaded,butwe’llcoverthatlaterinthechapter.)

Example6-1.$request->all()<!--GETrouteformviewat/get-route-->

<formmethod="post"action="/post-route?utm=12345">

{{csrf_field()}}

<inputtype="text"name="firstName">

<inputtype="submit">

</form>

Route::post('/post-route',function(Request$request){

var_dump($request->all());

});

//Outputs:

/**

*[

*'_token'=>'CSRFtokenhere',

*'firstName'=>'value',

*'utm'=>12345

*]

*/

Page 192: Laravel: Up and Running: A Framework for Building Modern PHP

$request->except()and$request->only()$request->except()providesthesameoutputas$request->all,butyoucanchooseoneormorefieldstoexclude—forexample,_token.Youcanpassiteitherastringoranarrayofstrings.

Example6-2showswhatitlookslikewhenweuse$request->except()onthesameformasinExample6-1.

Example6-2.$request->except()Route::post('/post-route',function(Request$request){

var_dump($request->except('_token'));

});

//Outputs:

/**

*[

*'firstName'=>'value',

*'utm'=>12345

*]

*/

$request->only()istheinverseof$request->except(),asyoucanseeinExample6-3.

Example6-3.$request->except()Route::post('/post-route',function(Request$request){

var_dump($request->only(['firstName','utm']));

});

//Outputs:

/**

*[

*'firstName'=>'value',

*'utm'=>12345

*]

*/

Page 193: Laravel: Up and Running: A Framework for Building Modern PHP

$request->has()and$request->exists()With$request->has()youcandetectwhetheraparticularpieceofuserinputisavailabletoyou.CheckoutExample6-4forananalyticsexamplewithourutmquerystringparameterfromthepreviousexamples.

Example6-4.$request->has()//POSTrouteat/post-route

if($request->has('utm')){

//Dosomeanalyticswork

}

$request->exists()and$request->has()differinthattheyhandleemptyvaluesdifferently:has()returnsFALSEifthekeyexistsandisempty;exists()returnsTRUEifthekeyexists,evenifit’sempty.

Page 194: Laravel: Up and Running: A Framework for Building Modern PHP

$request->input()Whereas$request->all(),$request->except(),and$request->only()operateonthefullarrayofinputprovidedbytheuser,$request->input()allowsyoutogetthevalueofjustasinglefield.Example6-5providesanexample.Notethatthesecondparameteristhedefaultvalue,soiftheuserhasn’tpassedinavalue,youcanhaveasensible(andnonbreaking)fallback.

Example6-5.$request->input()Route::post('/post-route',function(Request$request){

$userName=$request->input('name','(anonymous)');

});

Page 195: Laravel: Up and Running: A Framework for Building Modern PHP

ArrayInputLaravelalsoprovidesconveniencehelpersforaccessingdatafromarrayinput.Justusethe“dot”notationtoindicatethestepsofdiggingintothearraystructure,likeinExample6-6.

Example6-6.Dotnotationtoaccessarrayvaluesinuserdata<!--GETrouteformviewat/get-route-->

<formmethod="post"action="/post-route">

{{csrf_field()}}

<inputtype="text"name="employees[0][firstName]">

<inputtype="text"name="employees[0][lastName]">

<inputtype="text"name="employees[1][firstName]">

<inputtype="text"name="employees[1][lastName]">

<inputtype="submit">

</form>

//POSTrouteat/post-route

Route::post('/post-route',function(Request$request){

$employeeZeroFirstName=$request->input('employees.0.firstName');

$allLastNames=$request->input('employees.*.lastName');

$employeeOne=$request->input('employees.1');

});

//Ifformsfilledoutas"Jim""Smith""Bob""Jones":

//$employeeZeroFirstName='Jim';

//$allLastNames=['Smith','Jones'];

//$employeeOne=['firstName'=>'Bob','lastName'=>'Jones']

Page 196: Laravel: Up and Running: A Framework for Building Modern PHP

JSONInput(and$request->json())Sofarwe’vecoveredinputfromquerystrings(GET)andformsubmissions(POST).Butthere’sanotherformofuserinputthat’sbecomingmorecommonwiththeadventofJavaScriptsingle-pageapps(SPAs):theJSONrequest.It’sessentiallyjustaPOSTrequestwiththebodysettoJSONinsteadofatraditionalformPOST.

Let’stakealookatwhatitmightlookliketosubmitsomeJSONtoaLaravelroute,andhowtouse$request->input()topulloutthatdata(Example6-7).

Example6-7.GettingdatafromJSONwith$request->input()POST/post-routeHTTP/1.1

Content-Type:application/json

{

"firstName":"Joe",

"lastName":"Schmoe",

"spouse":{

"firstName":"Jill",

"lastName":"Schmoe"

}

}

//post-route

Route::post('post-route',function(Request$request){

$firstName=$request->input('firstName');

$spouseFirstname=$request->input('spouse.firstName');

});

Since$request->input()issmartenoughtopulluserdatafromGET,POST,orJSON,youmaywonderwhyLaravelevenoffers$request->json().Therearetworeasonsyoumightprefer$request->json().First,youmightwanttojustbemoreexplicittootherprogrammersonyourprojectaboutwhereyou’reexpectingthedatatocomefrom.Andsecond,ifthePOSTdoesn’thavethecorrectapplication/jsonheaders,$request->input()won’tpickitupasJSON,but$request->json()will.

FACADENAMESPACES,THEREQUEST( ) GLOBALHELPER ,ANDINJECTING$REQUEST

Anytimeyou’reusingfacadesinsideofnamespacedclasses(e.g.,controllers),you’llhavetoaddthefullfacadepathtotheimportblockatthetopofyourfile(e.g.,useIlluminate\Support\facades\Request).

Becauseofthis,severalofthefacadesalsohaveacompanionglobalhelperfunction.Ifthesehelperfunctionsarerunwithnoparameters,theyexposethesamesyntaxasthefacade(e.g.,request()->has()isthesameasRequest::has()).Theyalsohaveadefaultbehaviorforwhenyoupassthemaparameter(e.g.,request('firstName')isashortcuttorequest()->input('firstName')).

WithRequest,we’vebeencoveringinjectinganinstanceoftheRequestobject,butyoucouldalsousetheRequestfacadeortherequest()globalhelper.TakealookatChapter10tolearnmore.

Page 197: Laravel: Up and Running: A Framework for Building Modern PHP

RouteDataItmightnotbethefirstthingyouthinkofwhenyouimagine“userdata,”buttheURLisjustasmuchuserdataasanythingelseinthischapter.

Therearethreeprimarywaysyou’llgetdatafromtheURL:viatheRequestfacade,routeparameters,andRequestobjects.We’llcoverRequestobjectsinmoredetailinChapter10.

Page 198: Laravel: Up and Running: A Framework for Building Modern PHP

FromRequestInjectedRequestobjects(andtheRequestfacadeandtherequest()helper)haveseveralmethodsavailabletorepresentthestateofthecurrentpage’sURL,butrightnowlet’slookprimarilyatgettinginformationabouttheURLsegments.

Ifyou’renotfamiliarwiththeideaofURLsegments,eachgroupofcharactersafterthedomainiscalledasegment.So,http://www.myapp.com/users/15/hastwosegments:usersand15.

Asyoucanprobablyguess,wehavetwomethodsavailabletous:$request->segments()returnsanarrayofallsegments,and$request->segment($segmentId)allowsustogetthevalueofasinglesegment.Notethatsegmentsarereturnedona1-basedindex,sointheprecedingexample,$request->segment(1)wouldreturnusers.

Requestobjects,theRequestfacade,andtherequest()globalhelperprovidequiteafewmoremethodstohelpusgetdataoutoftheURL.Tolearnmore,checkoutChapter10.

Page 199: Laravel: Up and Running: A Framework for Building Modern PHP

FromRouteParametersTheotherprimarywaywegetdataabouttheURLisfromrouteparameters,whichareinjectedintothecontrollermethodorclosurethatisservingacurrentrouteasshowninExample6-8.

Example6-8.GettingURLdetailsfromrouteparameters//routes/web.php

Route::get('users/{id}',function($id){

//Iftheuservisitsmyapp.com/users/15/,$idwillequal15

});

Tolearnmoreaboutroutesandroutebinding,checkoutChapter3.

Page 200: Laravel: Up and Running: A Framework for Building Modern PHP

UploadedFilesWe’vetalkedaboutdifferentwaystointeractwithusers’textinput,butthere’salsothematteroffileuploadstoconsider.TheRequestfacadeprovidesaccesstoanyuploadedfilesusingtheRequest::file()method,whichtakesthefile’sinputnameasaparameterandreturnsaninstanceofSymfony\Component\HttpFoundation\File\UploadedFile.

Let’swalkthroughanexample.First,ourform,inExample6-9.

Example6-9.Aformtouploadfiles<formmethod="post"enctype="multipart/form-data">

{{csrf_field()}}

<inputtype="text"name="name">

<inputtype="file"name="profile_picture">

<inputtype="submit">

</form>

Now,let’stakealookatwhatwegetfromrunning$request->all(),inExample6-10.Notethat$request->input('profile_picture')willreturnnull;weneedtouse$request->file('profile_picture')instead.

Example6-10.TheoutputfromsubmittingtheforminExample6-9Route::post('form',function(Request$request){

var_dump($request->all());

});

//Output:

//[

//"_token"=>"tokenhere"

//"name"=>"asdf"

//"profile_picture"=>UploadedFile{}

//]

Route::post('form',function(Request$request){

if($request->hasFile('profile_picture')){

var_dump($request->file('profile_picture'));

}

});

//Output:

//UploadedFile(details)

Page 201: Laravel: Up and Running: A Framework for Building Modern PHP

VALIDATINGAFILEUPLOAD

AsyoucanseeinExample6-10,wehaveaccessto$request->hasFile()toseewhethertheuseruploadedafile.WecanalsocheckwhetherthefileuploadwassuccessfulbyusingisValid()onthefileitself:

if($request->file('profile_picture')->isValid()){

//

}

BecauseisValid()iscalledonthefileitself,itwillerroriftheuserdidn’tuploadafile.So,tocheckforboth,you’dneedtocheckforthefile’sexistencefirst:

if(

$request->hasFile('profile_picture')&&

$request->file('profile_picture')->isValid()

){

//

}

Symfony’sUploadedFileclassextendsPHP’snativeSplFileInfowithmethodsallowingyoutoeasilyinspectandmanipulatethefile.Thislistisn’texhaustive,butitgivesyouatasteofwhatyoucando:

guessExtension()

getMimeType()

store($path,$storageDisk=defaultdisk)

storeAs($path,$newName,$storageDisk=defaultdisk)

storePublicly($path,$storageDisk=defaultdisk)

storePubliclyAs($path,$newName,$storageDisk=defaultdisk)

move($directory,$newName=null)

getClientOriginalName()

getClientOriginalExtension()

getClientMimeType()

guessClientExtension()

getClientSize()

getError()

Page 202: Laravel: Up and Running: A Framework for Building Modern PHP

isValid()

Asyoucansee,mostofthemethodshavetodowithgettinginformationabouttheuploadedfile,butthere’sonethatyou’lllikelyusemorethanalltheothers:store()(newinLaravel5.3),whichtakesthefilethatwasuploadedwiththerequestandstoresitinaspecifieddirectoryonyourserver.Itsfirstparameteristhedestinationdirectory,andtheoptionalsecondparameterwillbethestoragedisk(s3,local,etc.)tousetostorethefile.

YoucanseeacommonworkflowinExample6-11.

Example6-11.Commonfileuploadworkflowif($request->hasFile('profile_picture')){

$path=$request->profile_picture->store('profiles','s3');

auth()->user()->profile_picture=$path;

auth()->user()->save();

}

Ifyouneedtospecifythefilename,youcanusestoreAs()insteadofstore().Thefirstparameterisstillthepath;thesecondisthefilename,andtheoptionalthirdparameteristhestoragedisktouse.

Page 203: Laravel: Up and Running: A Framework for Building Modern PHP
Page 204: Laravel: Up and Running: A Framework for Building Modern PHP

PROPERFORMENCODINGFORFILEUPLOADSIfyougetnullwhenyoutrytogetthecontentsofafilefromyourrequest,youmight’veforgottentosettheencodingtypeonyourform.Makesuretoaddtheattributeenctype="multipart/form-data"onyourform:

<formmethod="post"enctype="multipart/form-data">

Page 205: Laravel: Up and Running: A Framework for Building Modern PHP

ValidationLaravelhasquiteafewwaysyoucanvalidateincomingdata.We’llcoverformrequestsinthenextsection,sothatleavesuswithtwoprimaryoptions:validatingmanuallyorusingthevalidate()methodinthecontroller.Let’sstartwiththesimpler,andmorecommon,validate().

Page 206: Laravel: Up and Running: A Framework for Building Modern PHP

validate()intheControllerUsingValidatesRequestsOutofthebox,allLaravelcontrollersusetheValidatesRequeststrait,whichprovidesaconvenientvalidate()method.Let’stakealookatwhatitlookslikeinExample6-12.

Example6-12.Basicusageofcontrollervalidation//routes/web.php

Route::get('recipes/create','RecipesController@create');

Route::post('recipes','RecipesController@store');

//app/Http/Controllers/RecipesController.php

<?php

namespaceApp\Http\Controllers;

useIlluminate\Http\Request;

classRecipesControllerextendsController

{

publicfunctioncreate()

{

returnview('recipes.create');

}

publicfunctionstore(Request$request)

{

$this->validate($request,[

'title'=>'required|unique:recipes|max:125',

'body'=>'required'

]);

//Recipeisvalid;proceedtosaveit

}

}

Weonlyhavefourlinesofcoderunningourvalidationhere,butthey’redoingalot.

First,we’reexplicitlydefiningthefieldsweexpectandapplyingrules(hereseparatedbythepipecharacter,|)toeachindividually.

Next,thevalidate()methodcheckstheincomingdatafromthe$request(whichmeansitcanuse$request->all()or$request->input()justlikewelearnedaboutearlierinthechapter)anddetermineswhetherornotitisvalid.

Ifthedataisvalid,thevalidatemethodendsandwecanmoveonwithyourcontrollermethod,savingthedataorwhateverelse.

Butifthedataisn’tvalid,itthrowsaValidationException.Thiscontainsinstructionstotherouterabouthowtohandlethisexception.IftherequestisAjax(orifit’srequestingJSONasaresponse),theexceptionwillcreateaJSONresponsecontainingthevalidationerrors.Ifnot,theexceptionwillreturnaredirecttothepreviouspage,togetherwithalloftheuserinputandthevalidationerrors—perfectforrepopulatingafailedformandshowingsomeerrors.

Page 207: Laravel: Up and Running: A Framework for Building Modern PHP

MOREONLARAVEL’SVALIDATIONRULES

Inourexampleshere(likeinthedocs)we’reusingthe“pipe”syntax:'fieldname':'rule|otherRule|anotherRule'.Butyoucanalsousethearraysyntaxtodothesamething:'fieldname':['rule','otherRule','anotherRule'].

Additionally,youcanvalidatenestedproperties.ThismattersifyouuseHTML’sarraysyntax,whichallowsyouto,forexample,havemultiple“users”onanHTMLform,eachwithanassociatedname.Here’showyouvalidatethat:

$this->validate($request,[

'user.name'=>'required',

'user.email'=>'required|email',

]);

Wedon’thaveenoughspacetocovereverypossiblevalidationrulehere,buthereareafewofthemostcommonrulesandtheirfunctions:

Requirethefieldrequired;required_if:anotherField,equalToThisValue;required_unless:anotherField,equalToThisValue

Fieldmustcontaincertaintypesofcharacteralpha,alpha_dash,alpha_num,numeric,integer

Fieldmustcontaincertainpatternsemail,active_url,ip

Datesafter:date,before:date(datecanbeanyvalidstringthatstrtotime()canhandle)

Numbersbetween:min,max,min:num,max:num,size:num(sizetestsagainstlengthforstrings,valueforintegers,countforarrays,orsizeinKBforfiles)

Imagedimensionsdimensions:min_width=XXX;canalsouseand/orcombinewithmax_width,min_height,max_height,width,height,andratio

Databasesexists:tableName,unique:tableName(expectstolookinthesametablecolumnasthefieldname;seethedocsforhowtocustomize)

Page 208: Laravel: Up and Running: A Framework for Building Modern PHP

ManualValidationIfyouarenotworkinginacontroller,orifforsomeotherreasonthepreviouslydescribedflowisnotagoodfit,youcanmanuallycreateaValidatorinstanceandcheckforsuccessorfailurelikeinExample6-13.

Example6-13.ManualvalidationRoute::get('recipes/create',function(){

returnview('recipes.create');

});

Route::post('recipes',function(Illuminate\Http\Request$request){

$validator=Validator::make($request->all(),[

'title'=>'required|unique:recipes|max:125',

'body'=>'required'

]);

if($validator->fails()){

returnredirect('recipes/create')

->withErrors($validator)

->withInput();

}

//Recipeisvalid;proceedtosaveit

});

Asyoucansee,wecreateaninstanceofavalidatorbypassingitourinputasthefirstparameterandthevalidationrulesasthesecondparameter.Thevalidatorexposesafails()methodthatwecancheckagainstandcanbepassedintothewithErrors()methodoftheredirect.

Page 209: Laravel: Up and Running: A Framework for Building Modern PHP

DisplayingValidationErrorMessagesWe’vealreadycoveredmuchofthisinChapter5,buthere’saquickrefresheronhowtodisplayerrorsfromvalidation.

Thevalidate()methodincontrollers(andthewithErrors()methodonredirectsthatitrelieson)flashesanyerrorstothesession.Theseerrorsaremadeavailabletotheviewyou’rebeingredirectedtointhe$errorsvariable.AndrememberthatasapartofLaravel’smagic,that$errorsvariablewillbeavailableeverytimeyouloadtheview,evenifit’sjustempty,soyoudon’thavetocheckifitexistswithisset().

ThatmeansyoucandosomethinglikeExample6-14oneverypage.

Example6-14.Echovalidationerrors@if($errors->any())

<ulid="errors">

@foreach($errors->all()as$error)

<li>{{$error}}</li>

@endforeach

</ul>

@endif

Page 210: Laravel: Up and Running: A Framework for Building Modern PHP

FormRequestsAsyoubuildoutyourapplications,youmightstartnoticingsomepatternsinyourcontrollermethods.Therearecertainpatternsthatarerepeated—forexample,inputvalidation,userauthenticationandauthorization,andpossibleredirects.Ifyoufindyourselfwantingastructuretonormalizeandextractthesecommonbehaviorsoutofyourcontrollermethods,youmaybeinterestedinLaravel’sformrequests.

Aformrequestisacustomrequestclassthatisintendedtomaptothesubmissionofaform,andtherequesttakestheresponsilibityforvalidatingtherequest,authorizingtheuser,andoptionallyredirectingtheuseruponafailedvalidation.Eachformrequestwillusually,butnotalways,explicitlymaptoasingleHTTPrequest—e.g.,“CreateComment.”

Page 211: Laravel: Up and Running: A Framework for Building Modern PHP

CreatingaFormRequestYoucancreateanewformrequestusingArtisan:

phpartisanmake:requestCreateCommentRequest

Younowhaveaformrequestobjectavailableatapp/Http/Requests/CreateCommentRequest.php.

Everyformrequestclassprovideseitheroneortwopublicmethods.Thefirstisrules(),whichneedstoreturnanarrayofvalidationrulesforthisrequest.Thesecond(optional)methodisauthorize();ifthisreturnstrue,theuserisauthorizedtoperformthisrequest,andiffalse,theuserisrejected.TakealookatExample6-15toseeasampleformrequest.

Example6-15.Sampleformrequest<?php

namespaceApp\Http\Requests;

useApp\BlogPost;

useApp\Http\Requests\Request;

classCreateCommentRequestextendsRequest

{

publicfunctionrules()

{

return[

'body'=>'required|max:1000'

];

}

publicfunctionauthorize()

{

$blogPostId=$this->route('blogPost');

returnauth()->check()&&BlogPost::where('id',$blogPostId)

->where('user_id',auth()->user()->id)->exists();

}

}

Therules()sectionofExample6-15isprettyself-explanatory,butlet’slookatauthorize()briefly.

We’regrabbingthesegmentfromtheroutenamedblogPost.That’simplyingtheroutedefinitionforthisrouteprobablylooksabitlikethis:Route::post('blogPosts/{blogPost}',function(){//Dostuff}).Asyoucansee,wenamedtherouteparameterblogPost,whichmakesitaccessibleinourRequestusing$this->route('parametername').

Wethenlookatwhethertheuserisloggedinand,ifso,whetheranyblogpostsexistwiththatidentifierthatareownedbythecurrentlylogged-inuser.We’llcoverwhatimplicationsthishasshortly,buttheimportantthingtoknowisthatreturningtruemeanstheuserisauthorizedtoperformthespecifiedaction(inthiscase,creatingacomment),andfalsemeanstheuserisnotauthorized.

Page 212: Laravel: Up and Running: A Framework for Building Modern PHP

UsingaFormRequestNowthatwe’vecreatedaformrequestobject,howdoweuseit?It’salittlebitofLaravelmagic.Anyroute(closureorcontrollermethod)thattypehintsaformrequestasoneofitsparameterswillbenefitfromthedefinitionsofthatformrequest.

Let’stryitout,inExample6-16.

Example6-16.UsingaformrequestRoute::post('comments',function(App\Http\Requests\CreateCommentRequest$request){

//Storecomment

});

Youmightbewonderingwherewecalltheformrequest,butLaraveldoesitforus.Itvalidatestheuserinputandauthorizestherequest.Iftheinputisinvalid,it’llactjustlikethein-controllervalidate()methodworks,redirectingtheusertothepreviouspagewiththeirinputpreservedandwiththeappropriateerrormessagespassedalong.Andiftheuserisnotauthorized,Laravelwillreturna403Forbiddenerrorandnotexecutetheroutecode.

Page 213: Laravel: Up and Running: A Framework for Building Modern PHP

EloquentModelMassAssignmentUntilnow,we’vebeenlookingatvalidatingatthecontrollerlevel,whichisabsolutelythebestplacetostart.Butyoucanalsofiltertheincomingdataatthemodellevel.

It’sacommonpatterntopasstheentiretyofaform’sinputdirectlytoadatabasemodel.InLaravel,thatmightlooklikeExample6-17.

Example6-17.PassingtheentiretyofaformtoanEloquentmodelRoute::post('posts',function(Request$request){

$newPost=Post::create($request->all());

});

We’reassumingherethattheenduseriskindandnotmalicious,andhaskeptonlythefieldswewanthimtoedit—maybetheposttitleorbody.

Butwhatifourendusercanguess,ordiscern,thatwehaveanauthor_idfieldonthatpoststable?Whatifheusedhisbrowsertoolstoaddanauthor_idfieldandsettheIDtobesomeoneelse’sID,andtheotherpersonimpersonatedtheotherpersonbycreatingfakeblogpostsattributedtoher?

Eloquenthasaconceptcalled“massassignment”thatallowsyoutoeitherwhitelistfieldsthatarefillableinthisway(usingthemodel’s$fillableproperty)orblacklistfieldsthataren’tfillable(usingthemodel’s$guardedproperty).CheckoutChapter8tolearnmore.

Inourexample,wemightwanttofilloutthemodellikeExample6-18tokeepourappsafe.

Example6-18.GuardinganEloquentmodelfrommischeviousmassassignment<?php

namespaceApp;

useIlluminate\Database\Eloquent\Model;

classPostextendsModel

{

//Disablemassassignmentontheauthor_idfield

protected$guarded=['author_id'];

}

Bysettingauthor_idtoguarded,weensurethatmalicioususerswillnolongerbeabletooverridethevalueofthisfieldbymanuallyaddingittothecontentsofaformthatthey’resendingtoourapp.

Page 214: Laravel: Up and Running: A Framework for Building Modern PHP

DOUBLEPROTECTIONUSING$REQUEST->ONLY()Whileit’simportanttodoagoodjobofprotectingourmodelsfrommassassignment,it’salsoworthbeingcarefulontheassigningend.Ratherthanusing$request->all(),consider$request->only()soyoucanspecifywhichfieldsyou’dliketopassintoyourmodel:

Route::post('posts',function(Request$request){

$newPost=Post::create($request->only([

'title',

'body'

]));

});

Page 215: Laravel: Up and Running: A Framework for Building Modern PHP

{{Versus{!!Anytimeyoudisplaycontentonawebpagethatwascreatedbyauser,youneedtoguardagainstmaliciousinput,suchasscriptinjection.

Let’ssayyouallowyouruserstowriteblogpostsonyoursite.Youprobablydon’twantthemtobeabletoinjectmaliciousJavaScriptthatwillruninyourunsuspectingvisitors’browsers,right?So,you’llwanttoescapeanyuserinputthatyoushowonthepagetoavoidthis.

Thankfully,thisisalmostentirelycoveredforyou.IfyouuseLaravel’sBladetemplatingengine,thedefault“echo”syntax({{$stuffToEcho}})runstheoutputthroughhtmlentities()(PHP’sbestwayofmakingusercontentsafetoecho)automatically.Youactuallyhavetodoextraworktoavoidescapingtheoutput,byusingthe{!!$stuffToEcho!!}syntax.

Page 216: Laravel: Up and Running: A Framework for Building Modern PHP

TestingIfyou’reinterestedintestingyourinteractionswithuserinput,you’reprobablymostinterestedinsimulatingvalidandinvaliduserinputandensuringthatiftheinputisinvalidtheuserisredirected,andiftheinputisvalid,itendsupintheproperplace(e.g.,thedatabase).

Laravel’send-to-endapplicationtestingmakesthissimple.Let’sstartwithaninvalidroutethatweexpecttoberejected,inExample6-19.

Example6-19.Testingthatinvalidinputisrejectedpublicfunctiontest_input_missing_a_title_is_rejected()

{

$this->post('posts',['body'=>'Thisisthebodyofmypost']);

$this->assertRedirectedTo('posts/create');

$this->assertSessionHasErrors();

$this->assertHasOldInput();

}

Hereweassertthatafterinvalidinputtheuserisredirected,witherrorsandwiththeoldinputcorrectlypassedback.Youcanseewe’reusingafewcustomPHPUnitassertionsthatLaraveladdshere.

So,howdowetestourroute’ssuccess?CheckoutExample6-20.

Example6-20.Testingthatvalidinputisprocessedpublicfunctiontest_valid_input_should_create_a_post_in_the_database()

{

$this->post('posts',['title'=>'PostTitle','body'=>'Thisisthebody']);

$this->seeInDatabase(['title'=>'PostTitle']);

}

Notethat,ifyou’retestingsomethingusingthedatabase,you’llneedtolearnmoreaboutdatabasemigrationsandtransactions.MoreonthatinChapter12.

Page 217: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRTherearealotofwaystogetthesamedata:theRequestfacade,therequest()globalhelper,andinjectinganinstanceofIlluminate\Http\Request.Eachexposestheabilitytogetallinput,someinput,orspecificpiecesofdata,andfilesandJSONinputcanhavesomespecialconsiderationsattimes.

URIpathsegmentsarealsoapossiblesourceofuserinput,andthey’realsoaccessibleviatherequesttools.

ValidationcanbeperformedmanuallywithValidator::make(),orautomaticallyusingthe$this->validate()controllermethodorformrequests.Eachautomatictool,uponfailedvalidation,redirectstheusertothepreviouspagewithalloldinputstoredanderrorspassedalong.

ViewsandEloquentmodelsalsoneedtobeprotectedfromnefarioususerinput.ProtectBladeviewsusingthedoublecurlybracesyntax({{}}),whichescapesuserinput,andprotectmodelsbyonlypassingspecificfieldsintobulkmethodsusing$request->only()andbydefiningthemassassignmentrulesonthemodelitself.

Page 218: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter7.ArtisanandTinker

Frominstallationonward,modernPHPframeworksexpectmanyinteractionstotakeplaceonthecommandline.Laravelprovidesthreeprimarytoolsforcommand-lineinteraction:Artisan,asuiteofbuilt-incommand-lineactionswiththeabilitytoaddmore;Tinker,aREPLorinteractiveshellforyourapplication;andtheinstaller,whichwe’vealreadycoveredinChapter2.

Page 219: Laravel: Up and Running: A Framework for Building Modern PHP

AnIntroductiontoArtisanIfyou’vebeenreadingthroughthisbookchapterbychapter,you’vealreadylearnedhowtouseArtisancommands.Theylooksomethinglikethis:

phpartisanmake:controllerPostsController

Ifyoulookintherootfolderofyourapplication,you’llseethatartisanisactuallyjustaPHPfile.That’swhyyou’restartingyourcallwithphpartisan;you’repassingthatfileintoPHPtobeparsed.EverythingafterthatisjustpassedintoArtisanasarguments.

Page 220: Laravel: Up and Running: A Framework for Building Modern PHP
Page 221: Laravel: Up and Running: A Framework for Building Modern PHP

SYMFONYCONSOLESYNTAXArtisanisactuallyalayerontopoftheSymfonyConsolecomponent,soifyou’refamiliarwithwritingSymfonyConsolecommandsyoushouldberightathome.

SincethelistofArtisancommandsforanapplicationcanbechangedbyapackageorbythespecificcodeoftheapplication,it’sworthcheckingeverynewapplicationyouencountertoseewhatcommandsareavailable.

TogetalistofallavailableArtisancommands,youcanrunphpartisanlistfromtheprojectroot(althoughifyoujustrunphpartisanwithnoparameters,itwilldothesamething).

Page 222: Laravel: Up and Running: A Framework for Building Modern PHP

BasicArtisanCommandsThere’snotenoughspaceheretocoveralloftheArtisancommands,butwe’llcovermanyofthem.Let’sgetstartedwiththebasiccommands:

helpprovideshelpforacommand;e.g.,phpartisanhelpcommandName.

clear-compiledremovesLaravel’scompiledclassfile,whichislikeaninternalLaravelcache;runthisasafirstresortwhenthingsaregoingwrongandyoudon’tknowwhy.

downputsyourapplicationin“maintenancemode”inorderforyoutofixanerror,runmigrations,orwhateverelse;uprestoresanapplicationfrommaintenancemode.

envdisplayswhichenvironmentLaravelisrunningatthemoment;it’stheequivalentofechoingapp()->environment()in-app.

migraterunsalldatabasemigrations.

optimizeoptimizesyourapplicationforbetterperformancebycachingcorePHPclassesintobootstrap/cache/compile.php.

servespinsupaPHPserveratlocalhost:8000(youcancustomizethehostand/orportwith--hostand--port).

tinkerbringsuptheTinkerREPL,whichwe’llcoverlaterinthischapter.

Page 223: Laravel: Up and Running: A Framework for Building Modern PHP

OptionsBeforewecovertherestoftheArtisancommands,let’slookatafewnotableoptionsyoucanpassanytimeyourunanArtisancommand:

-qsuppressesalloutput.

-v,-vv,and-vvvarethethreelevelsofoutputverbosity(normal,verbose,anddebug).

--no-interactiondoesnotaskanyinteractivequestions,soitwon’tinterruptautomatedprocessesrunningit.

--envallowsyoutodefinewhichenvironmenttheArtisancommandshouldoperatein(e.g.,local,production,etc.).

--versionshowsyouwhichversionofLaravelyourapplicationisrunningon.

You’veprobablyguessedfromlookingattheseoptionsthatArtisancommandsareintendedtobeusedmuchlikebasicshellcommands:youmightrunthemmanually,buttheycanalsofunctionasapartofsomeautomatedprocessatsomepoint.

Forexample,therearemanyautomateddeployprocessesthatmightbenefitfromcertainArtisancommands.Youmightwanttorunphpartisanoptimizeeverytimeyoudeployanapplication.Flagslike-qand--no-interactionensurethatyourdeployscripts,notattendedbyahumanbeing,cankeeprunningsmoothly.

Page 224: Laravel: Up and Running: A Framework for Building Modern PHP

TheGroupedCommandsTherestofthecommandsavailableoutoftheboxaregroupedbycontext.Wewon’tcoverthemallhere,butwe’llcovereachcontextbroadly:

app

Thisjustcontainsapp:name,whichallowsyoutoreplaceeveryinstanceofthedefaulttop-levelApp\namespacewithanamespaceofyourchoosing.Forexample:phpartisanapp:nameMyApplication.

auth

Allwehavehereisauth:clear-resets,whichflushesalloftheexpiredpasswordresettokensfromthedatabase.

cache

cache:clearclearsthecaches,andcache:tablecreatesadatabasemigrationifyouplantousethedatabasecachedriver.

config

config:cachecachesyourconfigurationsettingsforfasterlookup;toclearthecache,useconfig:clear.

db

db:seedseedsyourdatabase,ifyouhaveconfigureddatabaseseeders.

event

event:generatebuildsmissingeventandeventlistenerfilesbasedonthedefinitionsinEventServiceProvider.We’lllearnmoreabouteventsinChapter16.

key

key:generatecreatesarandomapplicationencryptionkeyinyour.envfile.

Page 225: Laravel: Up and Running: A Framework for Building Modern PHP
Page 226: Laravel: Up and Running: A Framework for Building Modern PHP

RERUNNINGARTISANKEY:GENERATEMEANSLOSINGENCRYPTIONKEYS

Onlyrunphpartisankey:generateonce—thefirsttimeyousetuptheapplicationinanewenvironment—becausethiskeyisusedtoencryptyourdata;ifyouchangeitafterdatahasbeenstored,thatdatawillallbecomeinaccessible.

make

make:authscaffoldsouttheviewsandcorrespondingroutesforalandingpage,auserdashboard,andloginandregisterpages.Alltherestofthemake:actionscreateasingleitem,andhaveparametersthatvaryaccordingly.Tolearnmoreaboutanyindividualcommand’sparameters,usehelptoreaditsdocumentation.Forexample,youcouldrunphpartisanhelpmake:migrationandlearnthatyoucanpass--create=tableNameHeretocreateamigrationthatalreadyhasthecreatetablesyntaxinthefile,asshownhere:phpartisanmake:migrationcreate_posts_table--create=posts.

migrate

Wesawamigratecommandearliertorunourmigrations,butherewecanrunalltheothermigration-relatedcommands.Createthemigrationstable(tokeeptrackofthemigrationsthatareexecuted)withmigrate:install,resetyourmigrationsandstartfromscratchwithmigrate:reset,resetyourmigrationsandrunthemallagainwithmigrate:refresh,rollbackjustonemigrationwithmigrate:rollback,orcheckthestatusofyourmigrationswithmigrate:status.

notifications

notifications:tablegeneratesamigrationthatcreatesthetablefordatabasenotifications.

queue

We’llcoverLaravel’squeuesinChapter16,butthebasicideaisthatyoucanpushjobsupintoremotequeuestobeexecutedoneafteranotherbyaworker.Thiscommandgroupprovidesallthetoolsyouneedtointeractwithyourqueues,likequeue:listentostartlisteningtoaqueue,queue:tabletocreateamigrationfordatabase-backedqueues,andqueue:flushtoflushallfailedqueuejobs.Therearequiteafewmore,whichwe’lllearnaboutinChapter16.

route

Ifyourunroute:list,you’llseethedefinitionsofeveryroutedefinedintheapplication,includingeachroute’sverb(s),path,name,controller/closureaction,andmiddleware.Youcancachetheroutedefinitionsforfasterlookupswithroute:cacheandclearyourcachewithroute:clear.

Page 227: Laravel: Up and Running: A Framework for Building Modern PHP

schedule

We’llcoverLaravel’scron-likeschedulerinChapter16,butinorderforittowork,youneedtosetthesystemcrontorunschedule:runonceaminute:

*****php/home/myapp.com/artisanschedule:run>>/dev/null2>&1

Asyoucansee,thisArtisancommandisintendedtoberunregularlyinordertopoweracoreLaravelservice.

session

session:tablecreatesamigrationforapplicationsusingdatabase-backedsessions.

storage

storage:linkcreatesasymboliclinkfrompublic/storagetostorage/app/public.ThisisacommonconventioninLaravelapps,tomakeiteasytoputuseruploads(orotherfilesthatcommonlyendupinstorage/app)somewherewherethey’llbeaccessibleatapublicURL.

vendor

SomeLaravel-specificpackagesneedto“publish”someoftheirassets,eithersothattheycanbeservedfromyourpublicdirectoryorsothatyoucanmodifythem.Eitherway,thesepackagesregisterthese“publishableassets”withLaravel,andwhenyourunvendor:publish,itpublishesthemtotheirspecifiedlocations.

view

Laravel’sviewrenderingengineautomaticallycachesyourviews.Itusuallydoesagoodjobofhandlingitsowncacheinvalidation,butifyouevernoticeit’sgottenstuck,runview:cleartoclearthecache.

Page 228: Laravel: Up and Running: A Framework for Building Modern PHP

WritingCustomArtisanCommandsNowthatwe’vecoveredtheArtisancommandsthatcomewithLaraveloutofthebox,let’stalkaboutwritingyourown.

First,youshouldknow:there’sanArtisancommandforthat!Runningphpartisanmake:commandYourCommandNamegeneratesanewArtisancommandinapp/Console/Commands/{YourCommandName}.php.

Page 229: Laravel: Up and Running: A Framework for Building Modern PHP
Page 230: Laravel: Up and Running: A Framework for Building Modern PHP

PHPARTISANMAKE:COMMANDThecommandsignatureformake:commandhaschangedafewtimes.Itwasoriginallycommand:make,butforawhilein5.2itwasconsole:makeandthenmake:console.

Finally,in5.3,it’ssettled:allofthegeneratorsareunderthemake:namespace,andthecommandtogeneratenewArtisancommandsisnowmake:command.

Yourfirstargumentshouldbetheclassnameofthecommand,andyoucanoptionallypassa--commandparametertodefinewhattheterminalcommandwillbe(e.g.,appname:action).

So,let’sdoit:

phpartisanmake:consoleWelcomeNewUsers--command=email:newusers

TakealookatExample7-1toseewhatyou’llget.

Example7-1.ThedefaultskeletonofanArtisancommand<?php

namespaceApp\Console\Commands;

useIlluminate\Console\Command;

classWelcomeNewUsersextendsCommand

{

/**

*Thenameandsignatureoftheconsolecommand.

*

*@varstring

*/

protected$signature='email:newusers';

/**

*Theconsolecommanddescription.

*

*@varstring

*/

protected$description='Commanddescription';

/**

*Createanewcommandinstance.

*

*@returnvoid

*/

publicfunction__construct()

{

parent::__construct();

}

/**

*Executetheconsolecommand.

*

*@returnmixed

*/

publicfunctionhandle()

{

//

}

}

Asyoucansee,it’sveryeasytodefinethecommandsignature,thehelptextitshowsin

Page 231: Laravel: Up and Running: A Framework for Building Modern PHP

commandlists,andthecommand’sbehavioroninstantiation(__construct())andonexecution(handle()).

Page 232: Laravel: Up and Running: A Framework for Building Modern PHP

RegisteringCommandsThere’sonesteplefttomakethisnewcommandusableinyourapplication:youneedtoregisterit.

Openapp/Console/Kernel.php.You’llseeanarrayofcommandclassnamesunderthe$commandsproperty.Toregisteryournewcommand,additsclasstothisarray.Youcanwriteitout,orjustusethe::classclassnameaccessorontheclassasinExample7-2.

Example7-2.RegisteringanewcommandintheconsolekernelclassKernelextendsConsoleKernel

{

/**

*TheArtisancommandsprovidedbyyourapplication.

*

*@vararray

*/

protected$commands=[

\App\Console\Commands\WelcomeNewUsers::class,

];

Page 233: Laravel: Up and Running: A Framework for Building Modern PHP
Page 234: Laravel: Up and Running: A Framework for Building Modern PHP

WRITINGCLOSURE-BASEDCOMMANDSIfyou’dprefertokeepyourcommanddefinitionprocesssimpler,youcanwritecommandsasclosuresinsteadofclassesbydefiningtheminroutes/console.php.Everythingwediscussinthischapterwillapplythesameway,butyouwilljustdefineandregisterthecommandsinasinglestepinthatfile:

//routes/console.php

Artisan::command(

'password:reset{userId}{--sendEmail}',

function($userId,$sendEmail){

//dosomething...

}

);

Page 235: Laravel: Up and Running: A Framework for Building Modern PHP

ASampleCommandWehaven’tcoveredmailorEloquentyet(seeChapter15formailandChapter8forEloquent),butthesamplehandle()methodinExample7-3shouldreadprettyclearly.

Example7-3.AsampleArtisancommandhandle()method...

classWelcomeNewUsersextendsCommand

{

publicfunctionhandle()

{

User::signedUpThisWeek()->each(function($user){

Mail::send(

'emails.welcome',

['name'=>$user->name],

function($m)use($user){

$m->to($user->email)->subject('Welcome!');

}

);

});

}

Noweverytimeyourunphpartisanemail:newusers,thiscommandwillgrabeveryuserthatsignedupthisweekandsendthemthewelcomeemail.

Ifyouwouldpreferinjectingyourmailanduserdependenciesinsteadofusingfacades,youcantypehinttheminthecommandconstructor,andLaravel’scontainerwillinjectthemforyouwhenthecommandisinstantiated.

TakealookatExample7-4toseewhatExample7-3mightlooklikeusingdependencyinjectionandextractingitsbehaviorouttoaserviceclass.

Example7-4.Thesamecommand,refactored...

classWelcomeNewUsersextendsCommand

{

publicfunction__construct(UserMailer$userMailer)

{

parent::__construct();

$this->userMailer=$userMailer

}

publicfunctionhandle()

{

$this->userMailer->welcomeNewUsers();

}

KEEPITSIMPLE

ItispossibletocallArtisancommandsfromtherestofyourcode,soyoucanusethemtoencapsulatechunksofapplicationlogic.ThisisaverycommonpracticeintheLaravelcommunity.

However,theLaraveldocsrecommendinsteadpackagingtheapplicationlogicintoaserviceclass,andinjectingthatserviceintoyourcommand.Consolecommandsareseenasbeingsimilartocontrollers:they’renotdomainclasses,they’retrafficcopsthatjustrouteincomingrequeststothecorrectbehavior.

Page 236: Laravel: Up and Running: A Framework for Building Modern PHP

ArgumentsandOptionsThe$signaturepropertyofthenewcommandlookslikeitmightjustcontainthecommandname.Butthispropertyisalsowhereyou’lldefineanyargumentsandoptionsforthecommand.There’saspecific,simplesyntaxyoucanusetoaddargumentsandoptionstoyourArtisancommands.

Beforewedigintothatsyntax,takealookatanexampleforsomecontext:

protected$signature='password:reset{userId}{--sendEmail}';

Arguments,required,optional,and/orwithdefaultsTodefinearequiredargument,surrounditwithbraces:

password:reset{userId}

Tomaketheargumentoptional,addaquestionmark:

password:reset{userId?}

Tomakeitoptionalandprovideadefault,use:

password:reset{userId=1}

Options,requiredvalues,valuedefaults,andshortcutsOptionsaresimilartoarguments,butthey’reprefixedwith--andcanbeusedwithnovalue.Toaddabasicoption,surrounditwithbraces:

password:reset{userId}{--sendEmail}

Ifyouroptionrequiresavalue,addan=toitssignature:

password:reset{userId}{--password=}

Andifyouwanttopassadefaultvalue,additafterthe=:

password:reset{userId}{--queue=default}

ArrayargumentsandarrayoptionsBothforargumentsandforoptions,ifyouwanttoacceptanarrayasinput,usethe*character:

password:reset{userIds*}

password:reset{--ids=*}

Page 237: Laravel: Up and Running: A Framework for Building Modern PHP

UsingarrayargumentsandparameterslooksabitlikeExample7-5.

Example7-5.UsingarraysyntaxwithArtisancommands//Argument

phpartisanpassword:reset123

//Option

phpartisanpassword:reset--ids=1--ids=2--ids=3

Page 238: Laravel: Up and Running: A Framework for Building Modern PHP
Page 239: Laravel: Up and Running: A Framework for Building Modern PHP

ARRAYARGUMENTSMUSTBETHELASTARGUMENTSinceanarrayargumentcaptureseveryparameterafteritsdefinitionandaddsthemasarrayitems,anarrayargumenthastobethelastargumentoroptionwithinanArtisancommand’ssignature.

InputdescriptionsRememberhowthebuilt-inArtisancommandscangiveusmoreinformationabouttheirparametersifweuseartisanhelp?Wecanprovidethatsameinformationaboutourcustomcommands.Justaddacolonandthedescriptiontextwithinthecurlybraces,likeinExample7-6.

Example7-6.DefiningdescriptiontextforArtisanargumentsandoptionsprotected$signature='password:reset

{userId:TheIDoftheuser}

{--sendEmail:Whethertosenduseranemail}';

Page 240: Laravel: Up and Running: A Framework for Building Modern PHP

UsingInputNowthatwe’vepromptedforthisinput,howdoweuseitinourcommand’shandle()method?Wehavetwooptionsforretrievingthevaluesofargumentsandoptions.

argument()$this->argument()withnoparametersreturnsanarrayofallarguments(thefirstarrayitemwillbethecommandname).Withaparameterpassed,it’llreturnthevalueoftheargumentspecified:

//withdefinition"password:reset{userId}":

phpartisanpassword:reset5

//$this->argument()returnsthisarray

[

"command":"password:reset",

"userId':"5",

]

//$this->argument('userId')returnsthisstring

"5"

option()$this->option()withnoparametersreturnsanarrayofalloptions,includingsomethatwillbydefaultbefalseornull.Withaparameter,it’llreturnthevalueoftheoptionspecified:

//withdefinition"password:reset{--userId=}":

phpartisanpassword:reset--userId=5

//$this->option()returnsthisarray

[

"userId"=>"5"

"help"=>false

"quiet"=>false

"verbose"=>false

"version"=>false

"ansi"=>false

"no-ansi"=>false

"no-interaction"=>false

"env"=>null

]

//$this->option('userId')returnsthisstring

"5"

Example7-7showsanArtisancommandusingargument()andoption()initshandle()method.

Example7-7.GettinginputfromanArtisancommandpublicfunctionhandle()

{

//Allarguments,includingthecommandname

$arguments=$this->argument();

//Justthe'userId'argument

$userid=$this->argument('userId');

Page 241: Laravel: Up and Running: A Framework for Building Modern PHP

//Alloptions,includingsomedefaultslike'no-interaction'and'env'

$options=$this->option();

//Justthe'sendEmail'option

$sendEmail=$this->option('sendEmail');

}

Page 242: Laravel: Up and Running: A Framework for Building Modern PHP

PromptsThereareafewmorewaystogetuserinputfromwithinyourhandle()code,andtheyallinvolvepromptingtheusertoenterinformationduringtheexecutionofyourcommand:

ask()

Promptstheusertoenterfreeformtext:

$email=$this->ask('Whatisyouremailaddress?');

secret()

Promptstheusertoenterfreeformtext,buthidesthetypingwithasterisks:

$password=$this->ask('WhatistheDBpassword?');

confirm()

Promptstheuserforayes/noanswer,andreturnsaboolean:

if($this->confirm('Doyouwanttotruncatethetables?')){

//

}

AllanswersexceptyorYwillbetreatedasa“no.”

anticipate()

Promptstheusertoenterfreeformtext,andprovidesautocompletesuggestions.Stillallowstheusertotypewhatevershewants:

$album=$this->anticipate('Whatisthebestalbumever?',[

"TheJoshuaTree","PetSounds","What'sGoingOn"

]);

choice()

Promptstheusertochooseoneoftheprovidedoptions.Thelastparameteristhedefaultiftheuserdoesn’tchoose:

$winner=$this->choice(

'Whoisthebestfootballteam?',

['Gators','Wolverines'],

0

);

Notethatthefinalparameter,thedefault,shouldbethearraykey.Sincewepassedanonassociativearray,thekeyfor“Gators”is0.Youcouldalsokeyyourarray,ifyou’dprefer:

Page 243: Laravel: Up and Running: A Framework for Building Modern PHP

$winner=$this->choice(

'Whoisthebestfootballteam?',

['gators'=>'Gators','wolverines'=>'Wolverines'],

'gators'

);

Page 244: Laravel: Up and Running: A Framework for Building Modern PHP

OutputDuringtheexecutionofyourcommand,youmightwanttowritemessagestotheuser.Themostbasicwaytodothisistouse$this->info()tooutputbasicgreentext:

$this->info('Yourcommandhasrunsuccessfully.');

Youalsohaveavailablethecomment()(orange),question()(highlightedteal),error()(highlightedred),andline()(uncolored)methodstoechotothecommandline.

Pleasenotethattheexactcolorsmayvaryfrommachinetomachine,buttheytrytobeinlinewiththelocalmachine’sstandardsforcommunicatingtotheenduser.

TableoutputThetablecomponentmakesitsimpletocreateASCIItablesfullofyourdata.TakealookatExample7-8.

Example7-8.OutputtingtableswithArtisancommands$headers=['Name','Email'];

$data=[

['Dhriti','[email protected]'],

['Moses','[email protected]']

];

//Or,youcouldgetsimilardatafromthedatabase:

//$data=App\User::all(['name','email'])->toArray();

$this->table($headers,$data);

NotethatExample7-8hastwosetsofdata:theheaders,andthedataitself.Bothcontaintwo“cells”per“row”;thefirstcellineachrowisthename,andthesecondistheemail.ThatwaythedatafromtheEloquentcall(whichisconstrainedtopullonlynameandemail)matchesupwiththeheaders.

TakealookatExample7-9toseewhatthetableoutputlookslike.

Example7-9.SampleoutputofanArtisantable+---------+--------------------+

|Name|Email|

+---------+--------------------+

|Dhriti|[email protected]|

|Moses|[email protected]|

+---------+--------------------+

ProgressbarsIfyou’veeverrunnpminstall,you’veseenacommand-lineprogressbarbefore.Let’sbuildoneinExample7-10.

Example7-10.SampleArtisanprogressbar$totalUnits=10;

$this->output->progressStart($totalUnits);

Page 245: Laravel: Up and Running: A Framework for Building Modern PHP

for($i=0;$i<$totalUnits;$i++){

sleep(1);

$this->output->progressAdvance();

}

$this->output->progressFinish();

Whatdidwedohere?First,weinformedthesystemhowmany“units”weneededtoworkthrough.Maybeaunitisauser,andyouhave350users.Thebarwillthendividetheentirewidthithasavailableonyourscreenby350,andincrementitby1/350theverytimeyourunprogressAdvance().Onceyou’redone,runprogressFinish()soitknowsit’sdonedisplayingtheprogressbar.

Page 246: Laravel: Up and Running: A Framework for Building Modern PHP

CallingArtisanCommandsinNormalCodeWhileArtisancommandsaredesignedtoberunfromthecommandline,youcanalsocallthemfromothercode.

TheeasiestwayistousetheArtisanfacade.YoucaneithercallacommandusingArtisan::call()(whichwillreturnthecommand’sexitcode),orqueueacommandusingArtisan::queue().

Bothtaketwoparameters:first,theterminalcommand(password:reset);andsecond,anarrayofparameterstopassit.TakealookatExample7-11toseehowitworkswithargumentsandoptions.

Example7-11.CallingArtisancommandsfromothercodeRoute::get('test-artisan',function(){

$exitCode=Artisan::call('password:reset',[

'userId'=>15,'--sendEmail'=>true

]);

});

Asyoucansee,argumentsarepassedbykeyingtotheargumentname,andoptionswithnovaluecanbepassedtrueorfalse.

YoucanalsocallArtisancommandsfromothercommands,using$this->call,(whichisthesameasArtisan::call(),or$this->callSilent,whichisthesamebutsuppressesalloutput).SeeExample7-12foranexample.

Example7-12.CallingArtisancommandsfromotherArtisancommandspublicfunctionhandle()

{

$this->callSilent('password:reset',[

'userId'=>15

]);

}

Finally,youcaninjectaninstanceoftheIlluminate\Contracts\Console\Kernelcontract,anduseitscall()method.

Page 247: Laravel: Up and Running: A Framework for Building Modern PHP

TinkerTinkerisaREPL,orread–eval–printloop.Ifyou’veeverusedIRBinRuby,you’llbefamiliarwithhowaREPLworks.

REPLsgiveyouaprompt,similartothecommand-lineprompt,thatmimicsa“waiting”stateofyourapplication.YoutypeyourcommandsintotheREPL,hitReturn,andthenexpectwhatyoutypedtobeevaluatedandtheresponseprintedout.

Example7-13providesaquicksampletogiveyouasenseofhowitworksandhowitmightbeuseful.WestarttheREPLwithphpartisantinkerandarethenpresentedwithablankprompt(>>>);everyresponsetoourcommandsisprintedonalineprefacedwith=>.

Example7-13.UsingTinkerphpartisantinker

>>>$user=newApp\User;

=>App\User:{}

>>>$user->email='[email protected]';

=>"[email protected]"

>>>$user->password=bcrypt('superSecret');

=>"$2y$10$TWPGBC7e8d1bvJ1q5kv.VDUGfYDnE9gANl4mleuB3htIY2dxcQfQ5"

>>>$user->save();

=>true

Asyoucansee,wecreatedanewuser,setsomedata,andsavedittothedatabase.Andthisisreal.Ifthiswereaproductionapplication,wewould’vejustcreatedabrandnewuserinoursystem.

ThismakesTinkeragreattoolforsimpledatabaseinteractions,fortryingoutnewideas,andforrunningsnippetsofcodewhenit’dbeapaintofindaplacetoputthemintheapplicationsourcefiles.

TinkerispoweredbyPsyShell,socheckthatouttoseewhatelseyoucandowithTinker.

Page 248: Laravel: Up and Running: A Framework for Building Modern PHP

TestingSinceyouknowhowtocallArtisancommandsfromcode,it’seasytodothatinatestandensurethatwhateverbehavioryouexpectedtobeperformedhasbeenperformedcorrectly,asinExample7-14.

Example7-14.CallingArtisancommandsfromatestpublicfunctiontest_empty_log_command_empties_logs_table()

{

DB::table('logs')->insert(['message'=>'Didsomething']);

Artisan::call('logs:empty');

$this->assertCount(0,DB::table('logs')->get());

}

Asalways,facadesareeasytoswapout,butifyoudon’twanttodothisyoucaninsteadinjectyourdependenciesintotheconstructoroftheArtisancommand,whichwillmakethemeasytoswapoutattesttime.

TheArtisanfacadeprovidesaccesstotheIlluminate\Contracts\Console\Kernelcontract,soifyouwanttoavoidusingthefacadeinyourcode,youcaninsteadinjectaninstanceofthatanduseitscall()method,asinExample7-15.

Example7-15.InjectingthekernelinsteadofusingtheArtisanfacadeuseIlluminate\Contracts\Console\Kernel;

...

classNightlyCleanupextendsJob

{

...

publicfunctionhandle(Kernel$kernel)

{

//...dootherstuff

$kernel->call('logs:empty');

}

Page 249: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRArtisancommandsareLaravel’scommand-linetools.Laravelcomeswithquiteafewoutofthebox,butit’salsoeasytocreateyourownArtisancommandsandcallthemfromthecommandlineoryourowncode.

TinkerisaREPLthatmakesitsimpletogetintoyourapplicationenvironmentandinteractwithrealcodeandrealdata.

Page 250: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter8.DatabaseandEloquent

Laravelprovidesasuiteoftoolsforinteractingwithyourapplication’sdatabases,butthemostnotableisEloquent,Laravel’sActiveRecordORM(object-relationalmapper).

EloquentisoneofLaravel’smostpopularandinfluentialfeatures.It’sagreatexampleofhowLaravelisdifferentfromthemajorityofPHPframeworks;inaworldofDataMapperORMsthatarepowerfulbutcomplex,Eloquentstandsoutforitssimplicity.There’soneclasspertable,whichisresponsibleforretrieving,representing,andpersistingdatainthattable.

WhetherornotyouchoosetouseEloquent,however,you’llstillgetatonofbenefitfromtheotherdatabasetoolsLaravelprovides.So,beforewedigintoEloquent,let’sstartbycoveringthebasicsofLaravel’sdatabasefunctionality:migrations,seeders,andthequerybuilder.

Thenwe’llcoverEloquent:definingyourmodels;inserting,updating,anddeleting;customizingyourresponseswithaccessors,mutators,andattributecasting;andfinallyrelationships.There’salotgoingonhere,andit’seasytogetoverwhelmed,butjusttakeitonestepatatimeandwe’llmakeitthrough.

Page 251: Laravel: Up and Running: A Framework for Building Modern PHP

ConfigurationBeforewegetintohowtouseLaravel’sdatabasetools,let’spauseforasecondandgooverhowtoconfigureyourdatabasecredentialsandconnections.

Theconfigurationfordatabaseaccesslivesinconfig/database.php.LikemanyotherconfigurationareasinLaravel,youcandefinemultiple“connections”andthendecidewhichthecodewillusebydefault.

Page 252: Laravel: Up and Running: A Framework for Building Modern PHP

DatabaseConnectionsBydefault,there’soneconnectionforeachoftheconnectiontypes,asyoucanseeinExample8-1.

Example8-1.Thedefaultdatabaseconnectionslist'connections'=>[

'sqlite'=>[

'driver'=>'sqlite',

'database'=>database_path('database.sqlite'),

'prefix'=>'',

],

'mysql'=>[

'driver'=>'mysql',

'host'=>env('DB_HOST','localhost'),

'database'=>env('DB_DATABASE','forge'),

'username'=>env('DB_USERNAME','forge'),

'password'=>env('DB_PASSWORD',''),

'charset'=>'utf8',

'collation'=>'utf8_unicode_ci',

'prefix'=>'',

'strict'=>false,

'engine'=>null,

],

'pgsql'=>[

'driver'=>'pgsql',

'host'=>env('DB_HOST','localhost'),

'database'=>env('DB_DATABASE','forge'),

'username'=>env('DB_USERNAME','forge'),

'password'=>env('DB_PASSWORD',''),

'charset'=>'utf8',

'prefix'=>'',

'schema'=>'public',

],

'sqlsrv'=>[

'driver'=>'sqlsrv',

'host'=>env('DB_HOST','localhost'),

'database'=>env('DB_DATABASE','forge'),

'username'=>env('DB_USERNAME','forge'),

'password'=>env('DB_PASSWORD',''),

'charset'=>'utf8',

'prefix'=>'',

],

]

Youcouldcreatenewnamedconnections,though,andstillbeabletosetthedrivers(MySQL,Postgres,etc.)inthosenewnamedconnections.So,whilethere’soneconnectionperdriverbydefault,that’snotaconstraint.

Eachconnectionallowsyoutodefinethepropertiesnecessaryforconnectingtoandcustomizingeachconnectiontype.

Thereareafewreasonsfortheideaofmultipledrivers.Tostartwith,the“connections”sectionasitcomesoutoftheboxisasimpletemplatethatmakesiteasytostartappsthatuseanyofthesupporteddatabaseconnectiontypes.Inmanyapps,youcanpickthedatabaseconnectionyou’llbeusing,filloutitsinformation,andevendeletetheothersifyou’dlike.Iusuallyjustkeepthemallthere,incaseImighteventuallyusethem.

Page 253: Laravel: Up and Running: A Framework for Building Modern PHP

Buttherearealsosomecaseswhereyoumightneedmultipleconnectionswithinthesameapplication.Forexample,youmightusedifferentdatabaseconnectionsfortwodifferenttypesofdata,oryoumightreadfromoneandwritetoanother.Supportformultipleconnectionsmakesthispossible.

Page 254: Laravel: Up and Running: A Framework for Building Modern PHP

OtherDatabaseConfigurationOptionsTheconfig.databaseconfigurationsectionhasquiteafewotherconfigurationsettings.YoucanconfigureRedisaccess,customizethetablenameusedformigrations,determinethedefaultconnection,andtogglewhethernon-EloquentcallsreturnstdClassorarrayinstances.

WithanyserviceinLaravelthatallowsmultiple“connections”—sessionscanbebackedbythedatabaseorfilestorage,thecachecanuseRedisorMemcached,databasescanuseMySQLorPostgreSQL—youcandefinemultipleconnectionsandalsochoosethataparticularconnectionwillbethe“default,”meaningitwillbeusedanytimeyoudon’texplicitlyaskforaparticularconnection.Here’showyouaskforaspecificconnection,ifyouwantto:

$users=DB::connection('secondary')->select('select*fromusers');

Page 255: Laravel: Up and Running: A Framework for Building Modern PHP

MigrationsModernframeworkslikeLaravelmakeiteasytodefineyourdatabasestructurewithcode-drivenmigrations.Everynewtable,column,index,andkeycanbedefinedincode,andanynewenvironmentcanbebroughtfrombaredatabasetoyourapp’sperfectschemainseconds.

Page 256: Laravel: Up and Running: A Framework for Building Modern PHP

DefiningMigrationsAmigrationisasinglefilethatdefinestwothings:themodificationsdesiredwhenrunningthismigrationupandthemodificationsdesiredwhenrunningthismigrationdown.

“UP”AND“DOWN”INMIGRATIONS

Migrationsarealwaysruninorderbydate.Everymigrationfileisnamedsomethinglikethis:2014_10_12_000000_create_users_table.php.Whenanewsystemismigrated,thesystemgrabseachmigration,startingattheearliestdate,andrunsitsup()method—you’remigratingit“up”atthispoint.Butthemigrationsystemalsoallowsyouto“rollback”yourmostrecentsetofmigrations.It’llgrabeachofthemandrunitsdown()method,whichshouldundowhateverchangestheupmigrationmade.

So,theup()methodofamigrationshould“do”itsmigration,andthedown()methodshould“undo”it.

Example8-2showswhatthedefault“createuserstable”migrationthatcomeswithLaravellookslike.

Example8-2.Laravel’sdefault“createuserstable”migration<?php

useIlluminate\Database\Schema\Blueprint;

useIlluminate\Database\Migrations\Migration;

classCreateUsersTableextendsMigration

{

/**

*Runthemigrations.

*

*@returnvoid

*/

publicfunctionup()

{

Schema::create('users',function(Blueprint$table){

$table->increments('id');

$table->string('name');

$table->string('email')->unique();

$table->string('password',60);

$table->rememberToken();

$table->timestamps();

});

}

/**

*Reversethemigrations.

*

*@returnvoid

*/

publicfunctiondown()

{

Schema::drop('users');

}

}

Asyoucansee,wehaveanup()methodandadown()method.up()tellsthemigrationtocreateanewtablenameduserswithafewfields,anddown()tellsittodroptheuserstable.

CreatingamigrationAswesawinChapter7,there’sanArtisancommandforcreatingamigrationfile.It’sphp

Page 257: Laravel: Up and Running: A Framework for Building Modern PHP

artisanmake:migration,andithasasingleparameter,whichisthenameofthemigration.Forexample,tocreatethetablewejustcovered,youwouldrunphpartisanmake:migrationcreate_users_table.

Therearetwoflagsyoucanoptionallypasstothiscommand.--create=table_nameprefillsthemigrationwithcodedesignedtocreateatablenamedtable_name,and--table=_table_name_justprefillsthemigrationformodificationstoanexistingtable.Hereareafewexamples:

phpartisanmake:migrationcreate_users_table

phpartisanmake:migrationadd_votes_to_users_table--table=users

phpartisanmake:migrationcreate_users_table--create=users

CreatingtablesWealreadysawinthedefaultcreate_users_tablemigrationthatourmigrationsdependontheSchemafacadeanditsmethods.EverythingwecandointhesemigrationswillrelyonthemethodsofSchema.

Tocreateanewtableinamigration,usethecreate()method—thefirstparameteristhetablename,andthesecondisaclosurethatdefinesitscolumns:

Schema::create('tablename',function(Blueprint$table){

//Createcolumnshere

});

CreatingcolumnsTocreatenewcolumnsinatable,whetherinacreatetablecalloramodifytablecall,usetheinstanceofBlueprintthat’spassedintoyourclosure:

Schema::create('users',function(Blueprint$table){

$table->string('name');

});

Let’slookatthevariousmethodsavailableonBlueprintinstancesforcreatingcolumns.I’lldescribehowtheyworkinMySQL,butifyou’reusinganotherdatabase,Laravelwilljustusetheclosestequivalent.

ThefollowingarethesimplefieldBlueprintmethods:

integer(colName),tinyInteger(colName),smallInteger(colName),mediumInteger(colName),bigInteger(colName)

AddsanINTEGERtypecolumn,oroneofitsmanyvariations

string(colName,OPTIONALlength)

AddsaVARCHARtypecolumn

Page 258: Laravel: Up and Running: A Framework for Building Modern PHP

binary(colName)

AddsaBLOBtypecolumn

boolean(colName)

AddsaBOOLEANtypecolumn(aTINYINT(1)inMySQL)

char(colName,length)

AddsaCHARcolumn

datetime(colName)

AddsaDATETIMEcolumn

decimal(colName,precision,scale)

AddsaDECIMALcolumn,withprecisionandscale—e.g.,decimal('amount',5,2)specifiesaprecisionof5andascaleof2

double(colName,totaldigits,digitsafterdecimal)

AddsaDOUBLEcolumn—e.g.,double('tolerance',12,8)specifies12digitslong,with8ofthosedigitstotherightofthedecimalplace,asin7204.05691739

enum(colName,[choiceOne,choiceTwo])

AddsanENUMcolumn,withprovidedchoices

float(colName)

AddsaFLOATcolumn(sameasdoubleinMySQL)

json(colName)andjsonb(colName)AddsaJSONorJSONBcolumn(oraTEXTcolumninLaravel5.1)

text(colName),mediumText(colName),longText(colName)AddsaTEXTcolumn(oritsvarioussizes)

time(colName)

AddsaTIMEcolumn

timestamp(colName)

AddsaTIMESTAMPcolumn

uuid(colName)

AddsaUUIDcolumn(CHAR(36)inMySQL)

Andthesearethespecial(joined)Blueprintmethods:

increments(colName)andbigIncrements(colName)AddanunsignedincrementingINTEGERorBIGINTEGERprimarykeyID

timestamps()andnullableTimestamps()

Page 259: Laravel: Up and Running: A Framework for Building Modern PHP

Addscreated_atandupdated_attimestampcolumns

rememberToken()

Addsaremember_tokencolumn(VARCHAR(100))foruser“rememberme”tokens

softDeletes()

Addsadeleted_attimestampforusewithsoftdeletes

morphs(colName)

Foraprovided+colName+,addsanintegercolName_idandastringcolName_type(e.g.,morphs('tag')addsintegertag_idandstringtag_type);foruseinpolymorphicrelationships

BuildingextrapropertiesfluentlyMostofthepropertiesofafielddefinition—itslength,forexample—aresetasthesecondparameterofthefieldcreationmethodwelookedatintheprevioussection.Butthereareafewotherpropertiesthatwe’llsetbychainingmoremethodcallsafterthecreationofthecolumn.Forexample,thisemailfieldisnullableandwillbeplaced(inMySQL)rightafterthelast_namefield:

Schema::table('users',function(Blueprint$table){

$table->string('email')->nullable()->after('last_name');

});

Thefollowingmethodsareusedtosetadditionalpropertiesofafield:

nullable()

AllowsNULLvaluestobeinsertedintothiscolumn

default('defaultcontent')

Specifiesthedefaultcontentforthiscolumnifnovalueisprovided

unsigned()

Marksintegercolumnsasunsigned

first()(MySQLonly)Placesthecolumnfirstinthecolumnorder

after(colName)(MySQLonly)Placesthecolumnafteranothercolumninthecolumnorder

unique()

AddsaUNIQUEindex

primary()

Addsaprimarykeyindex

Page 260: Laravel: Up and Running: A Framework for Building Modern PHP

index()

AddsabasicindexNotethatunique(),primary(),andindex()canalsobeusedoutsideofthefluentcolumnbuildingcontext,whichwe’llcoverlater.

DroppingtablesIfyouwanttodropatable,there’sadropmethodonSchemathattakesoneparameter,thetablename:

Schema::drop('contacts');

ModifyingcolumnsTomodifyacolumn,justwritethecodeyouwouldwritetocreatethecolumnasifitwerenew,andthenappendacalltothechange()methodafterit.

Page 261: Laravel: Up and Running: A Framework for Building Modern PHP
Page 262: Laravel: Up and Running: A Framework for Building Modern PHP

REQUIREDDEPENDENCYBEFOREMODIFYINGCOLUMNSBeforeyoumodifyanycolumns(ordropanycolumnsinSQLite),you’llneedtoaddthedoctrine/dbalpackageasarequirementinyourcomposer.json,andruncomposerupdatetobringitin.

So,ifwehaveastringcolumnnamednamethathasalengthof255andwewanttochangeitslengthto100,thisishowwewouldwriteit:

Schema::table('users',function($table){

$table->string('name',100)->change();

});

Thesameistrueifwewanttoadjustanyofitspropertiesthataren’tdefinedinthemethodname.Tomakeafieldnullable,wedothis:

Schema::table('contacts',function($table){

$table->string('deleted_at')->nullable()->change();

});

Here’showwerenameacolumn:

Schema::table('contacts',function($table)

{

$table->renameColumn('promoted','is_promoted');

});

Andthisishowwedropacolumn:

Schema::table('contacts',function($table)

{

$table->dropColumn('votes');

});

Page 263: Laravel: Up and Running: A Framework for Building Modern PHP

MODIFYINGMULTIPLECOLUMNSATONCEINSQLITEIfyoutrytodropormodifymultiplecolumnswithinasinglemigrationclosureandyouareusingSQLite,you’llrunintoerrors.

InChapter12I’llrecommendthatyouuseSQLiteforyourtestingdatabase,soevenifyou’reusingamoretraditionaldatabase,youmaywanttoconsiderthisalimitationfortestingpurposes.

However,youdon’thavetocreateanewmigrationforeach.Instead,justcreatemultiplecallstoSchema::table()withintheup()methodofyourmigration:

publicfunctionup()

{

Schema::table('contacts',function(Blueprint$table)

{

$table->dropColumn('is_promoted');

});

Schema::table('contacts',function(Blueprint$table)

{

$table->dropColumn('alternate_email');

});

}

IndexesandforeignkeysWe’vecoveredhowtocreate,modify,anddeletecolumns.Let’smoveontoindexingandrelatingthem.

AddingindexesCheckoutExample8-3forexamplesofhowtoaddindexestoyourcolumn.

Example8-3.Addingcolumnindexesinmigrations//aftercolumnsarecreated...

$table->primary('primary_id');//Primarykey;unnecessaryifusedincrements()

$table->primary(['first_name','last_name']);//Compositekeys

$table->unique('email');//Uniqueindex

$table->unique('email','optional_custom_index_name');//Uniqueindex

$table->index('amount');//Basicindex

$table->index('amount','optional_custom_index_name');//Basicindex

Notethatthefirstexample(primary())isnotnecessaryifyou’reusingtheincrements()methodtocreateyourindex;thiswillautomaticallyaddaprimarykeyindexforyou.

RemovingindexesWecanremoveindexesasshowninExample8-4.

Example8-4.Removingcolumnindexesinmigrations$table->dropPrimary('contacts_id_primary');

$table->dropUnique('contacts_email_unique');

$table->dropIndex('optional_custom_index_name');

//IfyoupassanarrayofcolumnnamestodropIndex,itwill

//guesstheindexnamesforyoubasedonthegenerationrules

$table->dropIndex(['email','amount']);

Page 264: Laravel: Up and Running: A Framework for Building Modern PHP

AddingandremovingforeignkeysToaddaforeignkeythatdefinesthataparticularcolumnreferencesacolumnonanothertable,Laravel’ssyntaxissimpleandclear:

$table->foreign('user_id')->references('id')->on('users');

Herewe’readdingaforeignindexontheuser_idcolumn,showingthatitreferencestheidcolumnontheuserstable.Couldn’tgetmuchsimpler.

Ifwewanttospecifyforeignkeyconstraints,wecandothattoo,withonDelete()andonUpdate().Forexample:

$table->foreign('user_id')

->references('id')

->on('users')

->onDelete('cascade');

Todropanindex,wecaneitherdeleteitbyreferencingitsindexname(whichisautomaticallygeneratedbycombiningthenamesofthecolumnsandtablesbeingreferenced):

$table->dropForeign('contacts_user_id_foreign');

orbypassingitanarrayofthefieldsthatit’sreferencingonthelocaltable:

$table->dropForeign(['user_id']);

Page 265: Laravel: Up and Running: A Framework for Building Modern PHP

RunningMigrationsOnceyouhaveyourmigrationsdefined,howdoyourunthem?There’sanArtisancommandforthat:

phpartisanmigrate

Thiscommandrunsall“outstanding”migrations.Laravelkeepstrackofwhichmigrationsyouhaverunandwhichyouhaven’t.Everytimeyourunthiscommand,itcheckswhetheryou’verunallavailablemigrations,andifyouhaven’t,it’llrunanythatremain.

Thereareafewoptionsinthisnamespacethatyoucanworkwith.First,youcanrunyourmigrationsandyourseeds(whichwe’llcovernext):

phpartisanmigrate--seed

Youcanalsorunanyofthefollowingcommands:migrate:installcreatesthedatabasetablethatkeepstrackofwhichmigrationsyouhaveandhaven’trun;thisisrunautomaticallywhenyourunyourmigrations.

migrate:resetrollsbackeverydatabasemigrationyou’verunonthisinstall.

migrate:refreshrollsbackeverydatabasemigrationyou’verunonthisinstall,andthenrunseverymigrationavailable.It’sthesameasrunningmigrate:resetandthenmigrate,oneaftertheother.

migrate:rollbackrollsbackjustthemigrationsthatranthelasttimeyouranmigrate,or,withtheaddedoption--step=1,rollsbackthenumberofmigrationsyouspecify.

migrate:statusshowsatablelistingeverymigration,withaYorNnexttoeachshowingwhetherornotithasrunyetinthisenvironment.

Page 266: Laravel: Up and Running: A Framework for Building Modern PHP
Page 267: Laravel: Up and Running: A Framework for Building Modern PHP

MIGRATINGWITHHOMESTEAD/VAGRANTIfyou’rerunningmigrationsonyourlocalmachineandyour.envfilepointstoadatabaseinaVagrantbox,yourmigrationswillfail.You’llneedtosshintoyourVagrantboxandthenrunthemigrationsfromthere.ThesameistrueforseedsandanyotherArtisancommandsthataffectorreadfromthedatabase.

Page 268: Laravel: Up and Running: A Framework for Building Modern PHP

SeedingSeedingwithLaravelissosimple,ithasgainedwidespreadadoptionasapartofnormaldevelopmentworkflowsinawayithasn’tinpreviousPHPframeworks.There’sadatabase/seedsfolderthatcomeswithaDatabaseSeederclass,whichhasarun()methodthatiscalledwhenyoucalltheseeder.

Therearetwoprimarywaystoruntheseeders:alongwithamigration,orseparately.

Torunaseederalongwithamigration,justadd--seedtoanymigrationcall:

phpartisanmigrate--seed

phpartisanmigrate:refresh--seed

Andtorunitindependently:

phpartisandb:seed

phpartisandb:seed--class=VotesTableSeeder

Thiswillrunwhateveryouhavedefinedintherun()methodsofeveryseederclass(orjusttheclassyoupassedto--class).

Page 269: Laravel: Up and Running: A Framework for Building Modern PHP

CreatingaSeederTocreateaseeder,usethemake:seederArtisancommand:

phpartisanmake:seederContactsTableSeeder

You’llnowseeaContactsTableSeederclassshowupinthedatabase/seedsdirectory.Beforeweeditit,let’saddittotheDatabaseSeederclasssoitwillrunwhenwerunourseeders:

//database/seeds/DatabaseSeeder.php

...

publicfunctionrun()

{

$this->call(ContactsTableSeeder::class);

}

Nowlet’sedittheseederitself.ThesimplestthingwecandothereismanuallyinsertarecordusingtheDBfacade:

<?php

useIlluminate\Database\Seeder;

useIlluminate\Database\Eloquent\Model;

classContactsTableSeederextendsSeeder

{

publicfunctionrun()

{

DB::table('contacts')->insert([

'name'=>'LupitaSmith'

'email'=>'[email protected]',

]);

}

}

Thiswillgetusasinglerecord,whichisagoodstart.Butfortrulyfunctionalseeds,you’lllikelywanttoloopoversomesortofrandomgeneratorandrunthisinsert()manytimes,right?

Page 270: Laravel: Up and Running: A Framework for Building Modern PHP

ModelFactoriesModelfactoriesdefineone(ormore)patternsforcreatingfakeentriesforyourdatabasetables.Bydefaultthey’renamedafteranEloquentclass,butyoucanalsojustnamethemafterthetablenameifyou’renotgoingtoworkwithEloquent.Here’sthesametablesetupbothways:

$factory->define(User::class,function(Faker\Generator$faker){

return[

'name'=>$faker->name,

];

});

$factory->define('users',function(Faker\Generator$faker){

return[

'name'=>$faker->name,

];

});

Theoretiallyyoucannamethesefactoriesanythingyoulike,butnamingthefactoryafteryourEloquentclassisthemostidiomaticapproach.

CreatingamodelfactoryModelfactoriesaredefinedindatabase/factories/ModelFactory.php.Eachfactoryhasanameandadefinitionofhowtocreateanewinstanceofthedefinedclass.The$factory->define()methodtakesthefactorynameasthefirstparameterandaclosurethat’srunforeachgenerationasthesecondparameter.

Thesimplestfactorywecoulddefinemightlooksomethinglikethis:

$factory->define(Contact::class,function(Faker\Generator$faker){

return[

'name'=>'LupitaSmith',

'email'=>'[email protected]',

];

});

Nowwecanusethefactory()globalhelpertocreateaninstanceofContactinourseedingandtesting:

//Createone

$contact=factory(Contact::class)->create();

//Createmany

factory(Contact::class,20)->create();

However,ifweusedthatfactorytocreate20contacts,all20wouldhavethesameinformation.That’slessuseful.

WewillgetevenmorebenefitfrommodelfactorieswhenwetakeadvantageoftheinstanceofFakerthat’spassedintotheclosure;Fakermakesiteasytorandomizethecreationofstructuredfakedata.Thepreviousexamplenowturnsintothis:

Page 271: Laravel: Up and Running: A Framework for Building Modern PHP

$factory->define(Contact::class,function(Faker\Generator$faker){

return[

'name'=>$faker->name,

'email'=>$faker->email,

];

});

Now,everytimewecreateafakecontactusingthismodelfactory,allofourpropertieswillbeunique.

UsingamodelfactoryTherearetwoprimarycontextsinwhichwe’llusemodelfactories:testing(whichwe’llcoverinChapter12)andseeding,whichwe’retalkingabouthere.Let’swriteaseederusingamodelfactory;takealookatExample8-5.

Example8-5.Usingmodelfactoriesfactory(Post::class)->create([

'title'=>'Mygreatestpostever'

]);

factory(User::class,20)->create()->each(function($u)use($post){

$post->comments()->save(factory(Comment::class)->make([

'user_id'=>$u->id

]));

});

Whenwe’reusingafactory,weusethefactory()globalhelper,andpassitthenameofthefactory—which,aswejustsaw,isthenameoftheEloquentclasswe’regeneratinganinstanceof.Thatreturnsthefactory,andthenwecanrunoneoftwomethodsonit:make()orcreate().

Bothmethodsgenerateaninstanceofthisclass,usingthedefinitioninmodelFactory.php.Thedifferenceisthatmake()createstheinstancebutdoesn’t(yet)saveittothedatabase,whereascreate()savesittothedatabaseinstantly.

ThesecondexamplewillmakemoresenseoncewecoverrelationshipsinEloquentlaterinthischapter.

OverridingpropertieswhencallingamodelfactoryIfyoupassanarraytoeithermake()orcreate(),youcanoverridespecifickeys,likewedidinExample8-7tosettheuser_idonthecommentandtomanuallysetthetitleofourpost.

GeneratingmorethanoneinstancewithamodelfactoryIfyoupassanumberasthesecondparametertothefactory()helper,youcanspecifythatyou’recreatingmorethanoneinstance.Insteadofreturningasingleinstance,it’llreturnacollectionofinstances.Thismeansyoucantreattheresultlikeanarray,youcanassociateeachofitsinstanceswithanotherentity,oryoucanuseotherentitymethodsoneachinstance—likeweusedeach()inExample8-5toaddacommentfromeachnewlycreateduser.

Page 272: Laravel: Up and Running: A Framework for Building Modern PHP

DefiningandaccessingmultiplemodelfactorytypesLet’sgobacktomodelFactory.phpforasecond.WehaveaContactfactorydefined:

$factory->define(Contact::class,function(Faker\Generator$faker){

return[

'name'=>$faker->name,

'email'=>$faker->email,

];

});

Butsometimesyouneedmorethanonefactoryforaclassofobject.Whatifweneedtobeabletoaddsomecontactswhoareveryimportantpeople(VIPs)?Wecandefineasecondfactorytypeforthis,asseeninExample8-6.

Example8-6.Definingmultiplefactorytypesforthesamemodel$factory->define(Contact::class,function(Faker\Generator$faker){

return[

'name'=>$faker->name,

'email'=>$faker->email,

];

});

$factory->defineAs(Contact::class,'vip',function(Faker\Generator$faker){

return[

'name'=>$faker->name,

'email'=>$faker->email,

'vip'=>true,

];

});

Butthat’salotofduplication,right?Thankfully,wecanmakeanygivenmodelfactoryextendanother,andthenitcanjustoverrideoneorafewproperties.Let’shaveour“VIP”contactnowjustextendthepreviousbyusing$factory->raw(),asshowninExample8-7.

Example8-7.Extendingafactorytype$factory->define(Contact::class,function(Faker\Generator$faker){

return[

'name'=>$faker->name,

'email'=>$faker->email,

];

});

$factory->defineAs(

Contact::class,

'vip',

function(Faker\Generator$faker)use($factory){

$contact=$factory->raw(Contact::class);

returnarray_merge($contact,['vip'=>true]);

});

Now,let’smakeaspecifictype:

$vip=factory(Contact::class,'vip')->create();

$vips=factory(Contact::class,'vip',3)->create();

Page 273: Laravel: Up and Running: A Framework for Building Modern PHP

QueryBuilderNowthatyou’reconnectedandyou’vemigratedandseededyourtables,let’sgetstartedwithhowtousethedatabasetools.AtthecoreofeverypieceofLaravel’sdatabasefunctionalityisthequerybuilder,afluentinterfaceforinteractingwithyourdatabase.

WHATISAFLUENTINTERFACE?

AfluentinterfaceisonethatprimarilyusesmethodchainingtoprovideasimplerAPItotheenduser.Ratherthanexpectingalloftherelevantdatatobepassedintoeitheraconstructororamethodcall,fluentcallchainscanbebuiltgradually,withconsecutivecalls.Considerthiscomparison:

//Non-fluent:

$users=DB::select(['table'=>'users','where'=>['type'=>'donor']]);

//Fluent:

$users=DB::table('users')->where('type','donor')->get();

Laravel’sdatabasearchitecturecanconnecttoMySQL,Postgres,SQLite,andSQLServerthroughasingleinterface,withjustthechangeofafewconfigurationsettings.

Ifyou’veeverusedaPHPframework,you’velikelyusedatoolthatallowsyoutorun“raw”SQLquerieswithbasicescapingforsecurity.Thequerybuilderisthat,withalotofconveniencelayersandhelpersontop,solet’sstartthere.

Page 274: Laravel: Up and Running: A Framework for Building Modern PHP

BasicUsageoftheDBFacadeBeforewegetintobuildingcomplexquerieswithfluentmethodchaining,let’stakealookatafewsampleDBfacadecommands.TheDBfacadeisusedbothforquerybuilderchainingandforsimplerrawqueries,asillustratedinExample8-8.

Example8-8.SamplerawSQLandquerybuilderusage//basicstatement

DB::statement('droptableusers')

//rawselect,andparameterbinding

DB::select('select*fromcontactswherevalidated=?',[true]);

//selectusingthefluentbuilder

$users=DB::table('users')->get();

//joinsandothercomplexcalls

DB::table('users')

->join('contacts',function($join){

$join->on('users.id','=','contacts.user_id')

->where('contacts.type','donor');

})

->get();

Page 275: Laravel: Up and Running: A Framework for Building Modern PHP

RawSQLAswesawinExample8-1,it’spossibletomakeanyrawcalltothedatabaseusingtheDBfacadeandthestatement()method:DB::statement('SQLstatementhere').

Buttherearealsospecificmethodsforvariouscommonactions:select(),insert(),update(),anddelete().Thesearestillrawcalls,buttherearedifferences.First,usingupdate()anddelete()willreturnthenumberofrowsaffected,whereasstatement()won’t;second,withthesemethodsit’sclearertofuturedevelopersexactlywhatsortofstatementyou’remaking.

RawselectsThesimplestofthespecificDBmethodsisselect().Youcanrunitwithoutanyadditionalparameters:

$users=DB::select('select*fromusers');

ThiswillreturnacollectionofstdClassobjects.

ILLUMINATECOLLECTIONS

PriortoLaravel5.3,theDBfacadereturnedastdClassobjectformethodsthatreturnonlyonerow(likefirst()),andanarrayforanythatreturnmultiplerows(likeall()).InLaravel5.3,theDBfacade,likeEloquent,returnsacollectionforanymethodthatreturns(orcanreturn)multiplerows.TheDBfacadereturnsaninstanceofIlluminate\Support\CollectionandEloquentreturnsaninstanceofIlluminate\Database\Eloquent\Collection,whichextendsIlluminate\Support\CollectionwithafewEloquent-specificmethods.

CollectionislikeaPHParraywithsuperpowers,allowingyoutorunmap(),filter(),reduce(),each(),andmuchmoreonyourdata.YoucanlearnmoreaboutcollectionsinChapter17.

ParameterbindingsandnamedbindingsLaravel’sdatabasearchitectureallowsfortheuseofPDOparameterbinding,whichprotectsyourqueriesfrompotentialSQLattacks.Passingaparametertoastatementisassimpleasreplacingthevalueinyourstatementwitha?,thenaddingthevaluetothesecondparameterofyourcall:

$usersOfType=DB::select(

'select*fromuserswheretype=?',

[$type]

);

Youcanalsonamethoseparametersforclarity:

$usersOfType=DB::select(

'select*fromuserswheretype=:type',

['type'=>$userType]

);

Page 276: Laravel: Up and Running: A Framework for Building Modern PHP

RawinsertsFromhere,therawcommandsalllookprettymuchthesame.Rawinsertslooklikethis:

DB::insert(

'insertintocontacts(name,email)values(?,?)',

['sally','[email protected]']

);

RawupdatesUpdateslooklikethis:

$countUpdated=DB::update(

'updatecontactssetstatus=?whereid=?',

['donor',$id]

);

RawdeletesAnddeleteslooklikethis:

$countDeleted=DB::delete(

'deletefromcontactswherearchived=?',

[true]

);

Page 277: Laravel: Up and Running: A Framework for Building Modern PHP

ChainingwiththeQueryBuilderUpuntilnow,wehaven’tactuallyusedthequerybuilder,perse.We’vejustusedsimplemethodcallsontheDBfacade.Let’sactuallybuildsomequeries.

Thequerybuildermakesitpossibletochainmethodstogetherto,youguessedit,buildaquery.Attheendofyourchainyou’llusesomemethod—likelyget()—totriggertheactualexecutionofthequeryyou’vejustbuilt.

Let’stakealookataquickexample:

$usersOfType=DB::table('users')

->where('type',$type)

->get();

Here,webuiltourquery—userstable,$typetype—andthenweexecutedthequeryandgotourresult.

Let’stakealookatwhatmethodsthequerybuilderallowsyoutochain.ThemethodscanbesplitupintowhatI’llcallconstrainingmethods,modifyingmethods,andending/returningmethods.

ConstrainingmethodsThesemethodstakethequeryasitisandconstrainittoreturnasmallersubsetofpossibledata:

select()

Allowsyoutochoosewhichcolumnsyou’reselecting:

$emails=DB::table('contacts')

->select('email','email2assecond_email')

->get();

//Or

$emails=DB::table('contacts')

->select('email')

->addSelect('email2assecond_email')

->get();

where()

Allowsyoutolimitthescopeofwhat’sbeingreturnedusingWHERE.Bydefault,thesignatureofthewhere()methodisthatittakesthreeparameters—thecolumn,thecomparisonoperator,andthevalue:

$newContacts=DB::table('contact')

->where('created_at','>',Carbon::now()->subDay())

->get();

Page 278: Laravel: Up and Running: A Framework for Building Modern PHP

However,ifyourcomparisonis=,whichisthemostcommoncomparison,youcandropthesecondoperator:$vipContacts=DB::table('contacts')->where('vip',true)->get();.

Ifyouwanttocombinewhere()statements,youcaneitherchainthemaftereachother,orpassanarrayofarrays:

$newVips=DB::table('contacts')

->where('vip',true)

->where('created_at','>',Carbon::now()

->subDay());

//Or

$newVips=DB::table('contacts')->where([

['vip',true],

['created_at','>',Carbon::now()->subDay()],

]);

orWhere()

CreatessimpleORWHEREstatements:

$priorityContacts=DB::table('contacts')

->where('vip',true)

->orWhere('created_at','>',Carbon::now()->subDay())

->get();

TocreateamorecomplexORWHEREstatementwithmultipleconditions,passorWhere()aclosure:

$contacts=DB::table('contacts')

->where('vip',true)

->orWhere(function($query){

$query->where('created_at','>',Carbon::now()->subDay())

->where('trial',false);

})

->get();

Page 279: Laravel: Up and Running: A Framework for Building Modern PHP

POTENTIALCONFUSIONWITHMULTIPLEWHEREANDORWHERECALLS

IfyouareusingorWhere()callsinconjunctionwithmultiplewhere()calls,youneedtobeverycarefultoensurethequeryisdoingwhatyouthinkitis.Thisisn’tbecauseofanyfaultwithLaravel,butbecauseaquerylikethefollowingmightnotdowhatyouexpect:

$canEdit=DB::table('users')

->where('admin',true)

->orWhere('plan','premium')

->where('is_plan_owner',true)

->get();

SELECT*FROMusers

WHEREadmin=1

ORplan='premium'

ANDis_plan_owner=1;

IfyouwanttowriteSQLthatsays“ifthisOR(thisandthis),”whichisclearlytheintentioninthepreviousexample,you’llwanttopassaclosureintotheorWhere()call:

$canEdit=DB::table('users')

->where('admin',true)

->orWhere(function($query){

$query->where('plan','premium')

->where('is_plan_owner',true);

})

->get();

SELECT*FROMusers

WHEREadmin=1

OR(plan='premium'ANDis_plan_owner=1);

whereBetween(colName,[low,high])

Allowsyoutoscopeaquerytoreturnonlyrowswhereacolumnisbetweentwovalues(inclusiveofthetwovalues):

$mediumDrinks=DB::table('drinks')

->whereBetween('size',[6,12])

->get();

ThesameworksforwhereNotBetween(),butitwillselecttheinverse.

whereIn(colName,[1,2,3])

Allowsyoutoscopeaquerytoreturnonlyrowswhereacolumnisinanexplicitlyprovidedlistofoptions:$closeBy=DB::table('contacts')->whereIn(state,[FL,GA,AL])->get().

$closeBy=DB::table('contacts')->whereIn('state',['FL','GA','AL'])->get()

ThesameworksforwhereNotIn(),butitwillselecttheinverse.

Page 280: Laravel: Up and Running: A Framework for Building Modern PHP

whereNull(colName)andwhereNotNull(colName)AllowyoutoselectonlyrowswhereagivencolumnisNULLorisNOTNULL,respectively.

whereRaw()

Allowsyoutopassinaraw,unescapedstringtobeaddedaftertheWHEREstatement:$goofs=DB::table('contacts')->whereRaw('id=12345')->get().

Page 281: Laravel: Up and Running: A Framework for Building Modern PHP
Page 282: Laravel: Up and Running: A Framework for Building Modern PHP

BEWAREOFSQLINJECTION!AnySQLqueriespassedtowhereRaw()willnotbeescaped.Usethismethodcarefullyandinfrequently;thisistheprimeopportunityforSQLinjectionattacksinyourapp.

whereExists()

Allowsyoutoselectonlyrowsthat,whenpassedintoaprovidedsubquery,returnatleastonerow.Imagineyouonlywanttogetthoseuserswhohaveleftatleastonecomment:

$commenters=DB::table('users')

->whereExists(function($query){

$query->select('id')

->from('comments')

->whereRaw('comments.user_id=users.id');

})

->get();

distinct()

Selectsonlydistinctrows.Usuallythisispairedwithselect(),becauseifyouuseaprimarykey,therewillbenoduplicatedrows:$lastNames=DB::table('contacts')->select('last_name')->distinct()->get().

ModifyingmethodsThesemethodschangethewaythequery’sresultswillbeoutput,ratherthanjustlimitingitsresults:

orderBy(colName,direction)

Orderstheresults.Thesecondparametermaybeeitherasc(thedefault)ordesc:

$contacts=DB::table('contacts')

->orderBy('last_name','asc')

->get();

groupBy()andhaving()orhavingRaw()Groupsyourresultsbyacolumn.Optionally,having()andhavingRaw()allowyoutofilteryourresultsbasedonpropertiesofthegroups.Forexample,youcouldlookforonlycitieswithatleast30peopleinthem:

$populousCities=DB::table('contacts')

->groupBy('city')

->havingRaw('count(contact_id)>30')

->get();

skip()andtake()

Page 283: Laravel: Up and Running: A Framework for Building Modern PHP

Mostoftenusedforpagination,theseallowyoutodefinehowmanyrowstoreturnandhowmanytoskipbeforestartingthereturn—likeapagenumberandapagesizeinapaginationsystem:

$page4=DB::table('contacts')->skip(30)->take(10)->get();

latest(colName)andoldest(colName)Sortbythepassedcolumn(orcreated_atifnocolumnnameispassed)indescending(latest())orascending(oldest())order.

inRandomOrder()

Sortstheresultrandomly.

Ending/returningmethodsThesemethodsstopthequerychainandtriggertheexecutionoftheSQLquery:

get()

Getsallresultsforthebuiltquery:

$contacts=DB::table('contacts')->get();

$vipContacts=DB::table('contacts')->where('vip',true)->get();

first()andfirstOrFail()Getonlythefirstresult—likeget(),butwithaLIMIT1added:

$newestContact=DB::table('contacts')

->orderBy('created_at','desc')

->first();

first()

Failssilentlyiftherearenoresults,whereasfirstOrFail()willthrowanexception.Ifyoupassanarrayofcolumnnamestoeithermethod,they’llreturnthedataforjustthosecolumnsinsteadofallcolumns.

find(id)andfindOrFail(id)Likefirst(),butyoupassinanIDvaluethatcorrespondstotheprimarykeytolookup.find()failssilentlyifarowwiththatIDdoesn’texist,whilefindOrFail()willthrowanexception:

$contactFive=DB::table('contacts')->find(5);

Page 284: Laravel: Up and Running: A Framework for Building Modern PHP

value()

Plucksjustthevaluefromasinglefieldfromthefirstrow.Likefirst(),butifyouonlywantasinglecolumn:

$newestContactEmail=DB::table('contacts')

->orderBy('created_at','desc')

->value('email');

count()

Returnsanintegercountofallofthematchingresults:

$countVips=DB::table('contacts')

->where('vip',true)

->count();

min()andmax()Returntheminimumormaximumvalueofaparticularcolumn:

$highestCost=DB::table('orders')->max('amount');

sum()andavg()Returnthesumoraverageofallofthevaluesinaparticularcolumn:

$averageCost=DB::table('orders')

->where('status','completed')

->avg('amount');

WritingrawqueriesinsidequerybuildermethodswithDB::rawWe’vealreadyseenafewcustommethodsforrawstatements—forexample,select()hasaselectRaw()counterpartthatallowsyoutopassinastringforthequerybuildertoplaceaftertheWHEREstatement.

Youcanalso,however,passintheresultofaDB::raw()calltoalmostanymethodinthequerybuildertoachievethesameresult:

$contacts=DB::table('contacts')

->select(DB::raw('*,(score*100)ASinteger_score'))

->get();

JoinsJoinscansometimesbeapaintodefine,andthere’sonlysomuchaframeworkcandotomakethemsimpler,butthequerybuilderdoesitsbest.Let’slookatasample:

Page 285: Laravel: Up and Running: A Framework for Building Modern PHP

$users=DB::table('users')

->join('contacts','users.id','=','contacts.user_id')

->select('users.*','contacts.name','contacts.status')

->get();

Thejoin()methodcreatesaninnerjoin.Youcanalsochaintogethermultiplejoinsoneafteranother,oruseleftJoin()togetaleftjoin.

Finally,youcancreatemorecomplexjoinsbypassingaclosureintothejoin()method:

DB::table('users')

->join('contacts',function($join){

$join

->on('users.id','=','contacts.user_id')

->orOn('users.id','=','contacts.proxy_user_id');

})

->get();

UnionsYoucanuniontwoqueriestogetherbycreatingthemfirstandthenusingtheunion()orunionAll()methodtounionthem:

$first=DB::table('contacts')

->whereNull('first_name');

$contacts=DB::table('contacts')

->whereNull('last_name')

->union($first)

->get();

InsertsTheinsert()methodisprettysimple.Passitanarraytoinsertasingleroworanarrayofarraystoinsertmultiplerows,anduseinsertGetId()insteadofinsert()togettheautoincrementingprimarykeyIDbackasareturn:

$id=DB::table('contacts')->insertGetId([

'name'=>'AbeThomas',

'email'=>'[email protected]',

]);

DB::table('contacts')->insert([

['name'=>'TamikaJohnson','email'=>'[email protected]'],

['name'=>'JimPatterson','email'=>'[email protected]'],

]);

UpdatesUpdatesarealsosimple.Createyourupdatequeryand,insteadofget()orfirst(),justuseupdate()andpassitanarrayofparameters:

DB::table('contacts')

->where('points','>',100)

->update(['status'=>'vip']);

Page 286: Laravel: Up and Running: A Framework for Building Modern PHP

Youcanalsoquicklyincrementanddecrementcolumnsusingtheincrement()anddecrement()methods.Thefirstparameterofeachisthecolumnname,andthesecondis(optionally)thenumbertoincrement/decrementby:

DB::table('contacts')->increment('tokens',5);

DB::table('contacts')->decrement('tokens');

DeletesDeletesareevensimpler.Buildyourqueryandthenenditwithdelete():

DB::table('users')

->where('last_login','<',Carbon::now()->subYear())

->delete();

Youcanalsotruncatethetable,whichbothdeleteseveryrowandalsoresetstheautoincrementingID:

DB::table('contacts')->truncate();

JSONoperationsIfyouhaveJSONcolumns,youcanupdateorselectrowsbasedonaspectsoftheJSONstructurebyusingthearrowsyntaxtotraversechildren:

//Selectallrecordswherethe"isAdmin"propertyofthe"options"

//JSONcolumnissettotrue

DB::table('users')->where('options->isAdmin',true)->get();

//Updateallrecords,settingthe"verified"property

//ofthe"options"JSONcolumntotrue

DB::table('users')->update(['options->isVerified',true]);

ThisisanewfeatureinLaravel5.3.

Page 287: Laravel: Up and Running: A Framework for Building Modern PHP

TransactionsIfyou’renotfamiliarwithdatabasetransactions,they’reatoolthatallowsyoutowrapupaseriesofdatabasequeriestobeperformedinabatch,whichyoucanchoosetorollback,undoingtheentireseriesofqueries.Transactionsareoftenusedtoensurethatallornone,butnotsome,ofaseriesofrelatedqueriesareperformed—ifonefails,theORMwillrollbacktheentireseriesofqueries.

WiththeLaravelquerybuilder ’stransactionfeature,ifanyexceptionsarethrownatanypointwithinthetransactionclosure,allthequeriesinthetransactionwillberolledback.Ifthetransactionclosurefinishessuccessfully,allthequerieswillbecommittedandnotrolledback.

Let’stakealookatExample8-9.

Example8-9.AsimpledatabasetransactionDB::transaction(function()use($userId,$numVotes)

{

//PossiblyfailingDBquery

DB::table('users')

->where('id',$userId)

->update(['votes'=>$numVotes]);

//Cachingquerythatwedon'twanttoruniftheabovequeryfails

DB::table('votes')

->where('user_id',$userId)

->delete();

});

Weclearlyhadsomepreviousprocessthatsummarizedthenumberofvotesfromthevotestable.Wewanttocachethatnumberontheuserstableandthenwipethosevotesfromthevotestable.But,ofcourse,wedon’twanttowipethevotesuntiltheupdatetotheuserstablehasrunsuccessfully.Andwedon’twanttokeeptheupdatednumberofvotesontheuserstableifthevotestabledeletionfails.

Ifanythinggoeswrongwitheitherquery,theotherwon’tbeapplied.That’sthemagicofdatabasetransactions.

Notethatyoucanalsomanuallybeginandendtransactions—andthisappliesbothforquerybuilderqueriesandforEloquentqueries.StartwithDB::beginTransaction(),endwithDB::commit(),andabortwithDB::rollBack().

Page 288: Laravel: Up and Running: A Framework for Building Modern PHP

IntroductiontoEloquentEloquentisanActiveRecordORM,whichmeansit’sadatabaseabstractionlayerthatprovidesasingleinterfacetointeractwithmultipledatabasetypes.“ActiveRecord”meansthatasingleEloquentclassisresponsiblefornotonlyprovidingtheabilitytointeractwiththetableasawhole(e.g.,User::all()getsallusers),butalsorepresentinganindividualtablerow(e.g.,$sharon=newUser).Additionally,eachinstanceiscapableofmanagingitsownpersistence;youcancall$sharon->save()or$sharon->delete().

Eloquenthasaprimaryfocusonsimplicity,andliketherestoftheframework,itrelieson“conventionoverconfiguration”toallowyoutobuildpowerfulmodelswithminimalcode.

Forexample,youcanperformalloftheoperationsinExample8-11withthemodeldefinedinExample8-10.

Example8-10.ThesimplestEloquentmodel<?php

useIlluminate\Database\Eloquent\Model;

classContactextendsModel{}

Example8-11.OperationsachievablewiththesimplestEloquentmodelpublicfunctionsave(Request$request)

{

//Createandsaveanewcontactfromuserinput

$contact=newContact();

$contact->first_name=$request->input('first_name');

$contact->last_name=$request->input('last_name');

$conatct->email=$request->input('email');

$contact->save();

returnredirect('contacts');

}

publicfunctionshow($contactId)

{

//ReturnaJSONrepresentationofaContactbasedonaURLsegment;

//ifthecontactdoesn'texist,throwanexception

returnContact::findOrFail($contactId);

}

publicfunctionvips()

{

//Unnecessarilycomplexexample,butstillpossiblewithbasicEloquent

//class;addsa"formalName"propertytoeveryVIPentry

returnContact::where('vip',true)->get()->map(function($contact){

$contact->formalName="Theexalted{$contact->first_name}ofthe

{$contact->last_name}s";

return$contact;

});

}

How?Convention.Eloquentassumesthetablename(Contactbecomescontacts),andwiththatyouhaveafullyfunctionalEloquentmodel.

Let’scoverhowweworkwithEloquentmodels.

Page 289: Laravel: Up and Running: A Framework for Building Modern PHP

CreatingandDefiningEloquentModelsFirst,let’screateamodel.There’sanArtisancommandforthat:

phpartisanmake:modelContact

Thisiswhatwe’llget,inapp/Contact.php:

<?php

namespaceApp;

useIlluminate\Database\Eloquent\Model;

classContactextendsModel

{

//

}

Page 290: Laravel: Up and Running: A Framework for Building Modern PHP
Page 291: Laravel: Up and Running: A Framework for Building Modern PHP

CREATINGAMIGRATIONALONGWITHYOURMODELIfyouwanttoautomaticallycreateamigrationwhenyoucreateyourmodel,passthe-mor--migrationflag:

phpartisanmake:modelContact--migration

TablenameThedefaultbehaviorfortablenamesisthatLaravel“snakecases”andpluralizesyourclassname,soSecondaryContactwouldaccessatablenamedsecondary_contacts.Ifyou’dliketocustomizethename,setthe$tablepropertyexplicitlyonthemodel:

protected$table='contacts_secondary';

PrimarykeyLaravelassumes,bydefault,thateachtablewillhaveanautoincrementingintegerprimarykey,anditwillbenamedid.

Ifyouwanttochangethenameofyourprimarykey,changethe$primaryKeyproperty:

protected$primaryKey='contact_id';

Andifyouwanttosetittobenonincrementing,use:

public$incrementing=false;

TimestampsEloquentexpectseverytabletohavecreated_atandupdated_attimestampcolumns.Ifyourtablewon’thavethem,disablethe$timestampsfunctionality:

public$timestamps=false;

YoucancustomizetheformatEloquentusestostoreyourtimestampstothedatabasebysettingthe$dateFormatclasspropertytoacustomstring.ThestringwillbeparsedusingPHP’sdate()syntax,sothefollowingexamplewillstorethedateassecondssincetheUnixepoch:

protected$dateFormat='U';

Page 292: Laravel: Up and Running: A Framework for Building Modern PHP

RetrievingDatawithEloquentMostofthetimeyoupulldatafromyourdatabasewithEloquent,you’llusestaticcallsonyourEloquentmodel.

Let’sstartbygettingeverything:

$allContacts=Contact::all();

Thatwaseasy.Let’sfilteritabit:

$vipContacts=Contact::where('vip',true)->get();

WecanseethattheEloquentfacadegivesustheabilitytochainconstraints,andfromtheretheconstraintsgetveryfamiliar:

$newestContacts=Contact::orderBy('created_at','desc')

->take(10)

->get();

Itturnsoutthatonceyoumovepasttheinitialfacadename,you’rejustworkingwithLaravel’squerybuilder.Youcandoalotmore—we’llcoverthatsoon—buteverythingyoucandowiththequerybuilderontheDBfacadeyoucandoonyourEloquentobjects.

GetoneLikewecoveredearlierinthechapter,youcanusefirst()toreturnonlythefirstrecordfromaquery,orfind()topulljusttherecordwiththeprovidedID.Foreither,ifyouappend“orFail”tothemethodname,itwillthrowanexceptioniftherearenomatchingresults.ThismakesfindOrFail()acommontoolinlookingupanentitybyaURLsegment(orthrowinganexceptionifamatchingentitydoesn’texist)likeyoucanseeinExample8-12.

Example8-12.UsinganEloquentOrFail()methodinacontrollermethod//ContactController

publicfunctionshow($contactId)

{

returnview('contacts.show')

->with('contact',Contact::findOrFail($contactId));

}

Anysinglereturn(first(),firstOrFail(),find(),orfindOrFail())willreturnaninstanceoftheEloquentclass.So,Contact::first()willreturnaninstanceoftheclassContactwiththedatafromrow1fillingitout.

Page 293: Laravel: Up and Running: A Framework for Building Modern PHP

EXCEPTIONSAsyoucanseeinExample8-12,wedon’tneedtocatchEloquent’smodelnotfoundexception(Illuminate\Database\Eloquent\ModelNotFoundException)inourcontrollers;Laravel’sroutingsystemwillcatchthemandthrowa404forus.

Youcould,ofcourse,catchthatparticularexceptionandhandleit,ifyou’dlike.

Getmanyget()workswithEloquentjustlikeitdoesinnormalquerybuildercalls—buildaqueryandcallget()attheendtogettheresults:

$vipContacts=Contact::where('vip',true)->get();

However,thereisanEloquent-onlymethod,all(),whichyou’lloftenseepeopleusewhentheywanttogetanunfilteredlistofalldatainthetable:

$contacts=Contact::all();

Page 294: Laravel: Up and Running: A Framework for Building Modern PHP
Page 295: Laravel: Up and Running: A Framework for Building Modern PHP

USINGGET()INSTEADOFALL()Anytimeyoucanuseall(),youcoulduseget().Contact::get()hasthesameresponseasContact::all().However,themomentyoustartmodifyingyourquery—addingawhere()filter,forexample—all()willnolongerwork,butget()willcontinueworking.

So,eventhoughall()isverycommon,I’drecommendusingget()foreverything,andignoringthefactthatall()evenexists.

Theotherthingthat’sdifferentaboutEloquent’sget()methodisthat,priortoLaravel5.3,itreturnedanarrayinsteadofacollection.In5.3andlater,theybothreturncollections.

Chunkingresponseswithchunk()Ifyou’veeverneededtoprocessalargeamount(thousandsormore)ofrecordsatatime,youmayhaverunintomemoryorlockingissues.Laravelmakesitpossibletobreakyourrequestsintosmallerpieces(chunks)andprocesstheminbatches,keepingthememoryloadofyourlargerequestsmaller.Example8-13illustratestheuseofchunk().

Example8-13.ChunkinganEloquentquerytolimitmemoryusageContact::chunk(100,function($contacts){

foreach($contactsas$contact){

//Dosomethingwith$contact

}

});

AggregatesTheaggregatesthatareavailableonthequerybuilderareavailableonEloquentqueriesaswell.Forexample:

$countVips=Contact::where('vip',true)->count();

$sumVotes=Contact::sum('votes');

$averageSkill=User::avg('skill_level');

Page 296: Laravel: Up and Running: A Framework for Building Modern PHP

InsertsandUpdateswithEloquentInsertingandupdatingvaluesisoneoftheplaceswhereEloquentstartstodivergefromnormalquerybuildersyntax.

InsertsTherearetwoprimarywaystoinsertanewrecordusingEloquent.

First,youcancreateanewinstanceofyourEloquentclass,setyourpropertiesmanually,andcallsave()onthatinstance,likeinExample8-14.

Example8-14.InsertinganEloquentrecordbycreatinganewinstance$contact=newContact;

$contact->name='KenHirata';

$contact->email='[email protected]';

$contact->save();

//or

$contact=newContact([

'name'=>'KenHirata',

'email'=>'[email protected]'

]);

$contact->save();

Untilyousave(),thisinstanceofContactrepresentsthecontactfully—exceptithasneverbeensavedtothedatabase.Thatmeansitdoesn’thaveanid,iftheapplicationquitsitwon’tpersist,anditdoesn’thaveitscreated_atandupdated_atvaluesset.

YoucanalsopassanarraytoModel::create()toachievethesameoutput,asshowninExample8-15.

Example8-15.InsertinganEloquentrecordbypassinganarraytocreate()$contact=Contact::create([

'name'=>'KeahiHale',

'email'=>'[email protected]'

]);

Alsobeawarethatinanycontextwhereyouarepassinganarray(eithertonewModel(),Model::create(),orModel::update()),everypropertyyousetviaModel::create()hastobeapprovedfor“massassignment,”whichwe’llcovershortly.

Notethatifyou’reusingModel::create(),youdon’tneedtosave()theinstance—that’shandledasapartofthemodel’screate()method.

UpdatesUpdatingrecordslooksverysimilartoinserting.Youcangetaspecificinstance,changeitsproperties,andthensave,oryoucanmakeasinglecallandpassanarrayofupdatedproperties.Example8-16illustratesthefirstapproach.

Example8-16.UpdatinganEloquentrecordbyupdatinganinstanceandsaving$contact=Contact::find(1);

Page 297: Laravel: Up and Running: A Framework for Building Modern PHP

$contact->email='[email protected]';

$contact->save();

Sincethisrecordalreadyexists,itwillalreadyhaveacreated_attimestampandanid,whichwillstaythesame,buttheupdated_atfieldwillbechangedtothecurrentdateandtime.Example8-17illustratesthesecondapproach.

Example8-17.UpdatingoneormoreEloquentrecordsbypassinganarraytotheupdate()methodContact::where('created_at','<',Carbon::now()->subYear())

->update(['longevity'=>'ancient']);

//or

$contact=Contact::find(1);

$contact->update(['longevity'=>'ancient']);

Thismethodexpectsanarraywhereeachkeyisthecolumnnameandeachvalueisthecolumnvalue.

MassassignmentWe’velookedatafewexamplesofhowtopassarraysofvaluesintoEloquentclassmethods.However,noneofthesewillactuallyworkuntilyoudefinewhichfieldsare“fillable”onthemodel.

Thegoalofthisistoprotect(malicious)userinputfromaccidentallysettingnewvaluesonfieldsyoudon’twantchanged.ConsiderthecommonscenarioinExample8-18.

Example8-18.UpdatinganEloquentmodelusingtheentiretyofarequest’sinput//ContactController

publicfunctionupdate(Contact$contact,Request$request)

{

$contact->update($request->all());

}

Ifyou’renotfamiliarwiththeIlluminateRequestobject,Example8-18willtakeeverypieceofuserinputandpassittotheupdate()method.Thatall()methodincludesthingslikeURLparametersandforminputs,soamalicioususercouldeasilyaddsomethingsinthere,likeidandowner_id,thatyoulikelydon’twantupdated.

Thankfully,thatwon’tactuallyworkuntilyoudefineyourmodel’sfillablefields.Youcaneitherwhitelistthefillablefields,orblacklistthe“guarded”fieldstodeterminewhichfieldscanorcannotbeeditedvia“massassignment”—i.e.,bypassinganarrayofvaluesintoeithercreate()orupdate().Notethatnonfillablepropertiescanstillbechangedbydirectassignment(e.g.,$contact\->password='abc';).Example8-19showsbothapproaches.

Example8-19.UsingEloquent’sfillableorguardedpropertiestodefinemass-assignablefieldsclassContact

{

protected$fillable=['name','email'];

//or

Page 298: Laravel: Up and Running: A Framework for Building Modern PHP

protected$guarded=['id','created_at','updated_at','owner_id'];

}

Page 299: Laravel: Up and Running: A Framework for Building Modern PHP

USINGREQUEST::ONLY()WITHELOQUENTMASSASSIGNMENTInExample8-18,weneededEloquent’smassassignmentguardbecausewewereusingtheall()methodontherequestobjecttopasstheentiretyoftheuserinputintoourEloquentobject.

Eloquent’smassassignmentprotectionisagreattoolhere,butthere’salsoahelpfultricktokeepyoufromacceptingjustanyinputfromtheuser.

TheRequestclasshasanonly()methodthatallowsyoutopluckonlyafewkeysfromtheuserinput.Sonowyoucandothis:

Contact::create($request->only('name','email'));

firstOrCreate()andfirstOrNew()Sometimesyouwanttototellyourapplication,“Getmeaninstancewiththeseproperties,orifitdoesn’texist,createit.”ThisiswherethefirstOr*()methodscomein.

ThefirstOrCreate()andfirstOrNew()methodstakeanarrayofkeysandvaluesastheirfirstparameter:

$contact=Contact::firstOrCreate(['email'=>'[email protected]']);

They’llbothlookforandretrievethefirstrecordmatchingthoseparameters,andiftherearenomatchingrecords,they’llcreateaninstancewiththoseproperties;firstOrCreate()willpersistthatinstancetothedatabaseandthenreturnit,whilefirstOrNew()willreturnitwithoutsavingit.

Ifyoupassanarrayofvaluesasthesecondparameter,thosevalueswillbeaddedtothecreatedentry(ifit’screated),butwon’tbeusedtolookupwhethertheentryexists.

Page 300: Laravel: Up and Running: A Framework for Building Modern PHP

DeletingwithEloquentDeletingwithEloquentisverysimilartoupdatingwithEloquent,butwithsoftdeletes,youcanarchiveyourdeleteditemsforlaterinspectionorevenrecovery.

NormaldeletesThesimplestwaytodeleteaninstanceistocallthedelete()methodontheinstanceitself:

$contact=Contact::find(5);

$contact->delete();

However,ifyouonlyhavetheID,there’snoreasontolookupaninstancejusttodeleteit;youcanpassanIDoranarrayofIDstothemodel’sdestroy()methodtodeletethemdirectly:

Contact::destroy(1);

//or

Contact::destroy([1,5,7]);

Finally,youcandeletealloftheresultsofaquery:

Contact::where('updated_at','<',Carbon::now()->subYear())->delete();

SoftdeletesSoftdeletesmarkdatabaserowsasdeletedwithoutactuallydeletingthemfromthedatabase.Thisgivesyoutheabilitytoinspectthemlater;tohaverecordsthatshowmorethan“noinformation,deleted”whendisplayinghistoricinformation;andtoallowyourusers(oradmins)torestoresomeoralldata.

Thehardpartabouthandcodinganapplicationwithsoftdeletesisthateveryqueryyoueverwritewillneedtoexcludethesoft-deleteddata.Thankfully,ifyouuseEloquent’ssoftdeletes,everyqueryyouevermakewillbescopedtoignoresoftdeletesbydefault,unlessyouexplicitlyasktobringthemback.

Eloquent’ssoftdeletefunctionalityrequiresadeleted_atcolumntobeaddedtothetable.OnceyouenablesoftdeletesonthatEloquentmodel,everyqueryyoueverwrite(unlessyouexplicitlyincludesoft-deletedrecords)willbescopedtoignoresoft-deletedrows.

WHENSHOULDIUSESOFTDELETES?

Justbecauseafeatureexists,itdoesn’tmeanyoushouldalwaysuseit.ManyfolksintheLaravelcommunitydefaulttousingsoftdeletesoneveryprojectjustbecausethefeatureisthere.Therearerealcoststosoftdeletes,though.It’sprettylikelythat,ifyouviewyourdatabasedirectlyinatoollikeSequelPro,you’llforgettocheckthedeleted_atcolumnatleastonce.Andifyoudon’tcleanupoldsoft-deletedrecords,yourdatabaseswillgetlargerandlarger.

Here’smyrecommendation:don’tusesoftdeletesbydefault.Instead,usethemwhenyouneedthem,andwhenyoudo,cleanoutoldsoftdeletesasaggressivelyasyoucan.It’sapowerfultool,butnotworthusingunlessyouneedit.

Page 301: Laravel: Up and Running: A Framework for Building Modern PHP

EnablingsoftdeletesYouenablesoftdeletesbydoingthreethings:addingthedeleted_atcolumninamigration,importingtheSoftDeletestraitinthemodel,andaddingthedeleted_atcolumntoyour$datesproperty.There’sasoftDeletes()methodavailableonthequerybuildertoaddthedeleted_atcolumntoatable,asyoucanseeinExample8-20.AndExample8-21showsanEloquentmodelwithsoftdeletesenabled.

Example8-20.MigrationtoaddthesoftdeletecolumntoatableSchema::table('contacts',function(Blueprint$table){

$table->softDeletes();

});

Example8-21.AnEloquentmodelwithsoftdeletesenabled<?php

useIlluminate\Database\Eloquent\Model;

useIlluminate\Database\Eloquent\SoftDeletes;

classContactextendsModel

{

useSoftDeletes;//usethetrait

protected$dates=['deleted_at'];//markthiscolumnasadate

}

Onceyoumakethesechanges,everydelete()anddestroy()callwillnowsetthedeleted_atcolumnonyourrowtobethecurrentdateandtimeinsteadofdeletingthatrow.Andallfuturequerieswillexcludethatrowasaresult.

QueryingwithsoftdeletesSo,howdowegetsoft-deleteditems?

First,youcanaddsoft-deleteditemstoaquery:

$allHistoricContacts=Contact::withTrashed()->get();

Next,youcanusethetrashed()methodtoseeifaparticularinstancehasbeensoftdeleted:

if($contact->trashed()){

//dosomething

}

Finally,youcangetonlysoft-deleteditems:

$deletedContacts=Contact::onlyTrashed()->get();

Restoringsoft-deletedentitiesIfyouwanttorestoreasoft-deleteditem,youcanrunrestore()onaninstanceoraquery:

$contact->restore();

Page 302: Laravel: Up and Running: A Framework for Building Modern PHP

//or

Contact::onlyTrashed()->where('vip',true)->restore();

Force-deletingsoft-deletedentitiesYoucandeleteasoft-deletedentitybycallingforceDelete()onanentityorquery:

$contact->forceDelete();

//or

Contact::onlyTrashed()->forceDelete();

Page 303: Laravel: Up and Running: A Framework for Building Modern PHP

ScopesWe’vecovered“filtered”queries,meaninganyquerywherewe’renotjustreturningeveryresultforatable.Buteverytimewe’vewrittenthemsofarinthischapter,it’sbeenamanualprocessusingthequerybuilder.

LocalandglobalscopesinEloquentallowyoutodefineprebuilt“scopes”(filters)thatyoucanuseeithereverytimeamodelisqueried(“global”)oreverytimeyouqueryitwithaparticularmethodchain(“local”).

LocalscopesLocalscopesarethesimplesttounderstand.Let’stakethisexample:

$activeVips=Contact::where('vip',true)->where('trial',false)->get();

Firstofall,ifwewritethiscombinationofquerymethodsoverandover,itwillgettedious.Butadditionally,theknowledgeofhowtodefinesomeonebeingan“activeVIP”isnowspreadaroundourapplication.Wewanttocentralizethatknowledge.Whatifwecouldjustwritethis?

$activeVips=Contact::activeVips()->get();

Wecan—it’scalledalocalscope.Andit’seasytodefineontheContactclass:

classContact

{

publicfunctionscopeActiveVips($query)

{

return$query->where('vip',true)->where('trial',false);

}

Todefinealocalscope,weaddamethodtotheEloquentclassthatbeginswith“scope”andthencontainsthetitle-casedversionofthescopename.Thismethodispassedaquerybuilderandneedstoreturnaquerybuilder,butofcourseyoucanmodifythequerybeforereturning—that’sthewholepoint.

Youcanalsodefinescopesthatacceptparameters:

classContact

{

publicfunctionscopeStatus($query,$status)

{

return$query->where('status',$status);

}

Andyouusetheminthesameway,justpassingtheparametertothescope:

$friends=Contact::status('friend')->get();

Page 304: Laravel: Up and Running: A Framework for Building Modern PHP

GlobalscopesRememberhowwetalkedaboutsoftdeletesonlyworkingifyouscopeeveryqueryonthemodeltoignorethesoft-deleteditems?That’saglobalscope.Andwecandefineourownglobalscopes,whichwillbeappliedoneveryquerymadefromagivenmodel.

Therearetwowaystodefineaglobalscope:usingaclosureorusinganentireclass.Ineach,you’llregisterthedefinedscopeinthemodel’sboot()method.Let’sstartwiththeclosuremethod,illustratedinExample8-22.

Example8-22.Addingaglobalscopeusingaclosure...

classContactextendsModel

{

protectedstaticfunctionboot()

{

parent::boot();

static::addGlobalScope('active',function(Builder$builder){

$builder->where('active',true);

});

}

That’sit.Wejustaddedaglobalscope,namedactive,andeveryqueryonthismodelwillbescopedtoonlyrowswithactivesettotrue.

Next,let’strythelongerway,asshowninExample8-23.CreateaclassthatimplementsIlluminate\Database\Eloquent\Scope,whichmeansitwillhaveanapply()methodthattakesaninstanceofaquerybuilderandaninstanceofthemodel.

Example8-23.Creatingaglobalscopeclass<?php

namespaceApp\Scopes;

useIlluminate\Database\Eloquent\Scope;

useIlluminate\Database\Eloquent\Model;

useIlluminate\Database\Eloquent\Builder;

classActiveScopeimplementsScope

{

publicfunctionapply(Builder$builder,Model$model)

{

return$builder->where('active',true);

}

}

Toapplythisscopetoamodel,onceagainoverridetheparent’sboot()methodandcalladdGlobalScope()ontheclassusingstatic,asshowninExample8-24.

Example8-24.Applyingaclass-basedglobalscope<?php

useApp\Scopes\ActiveScope;

useIlluminate\Database\Eloquent\Model;

classContactextendsModel

{

protectedstaticfunctionboot()

Page 305: Laravel: Up and Running: A Framework for Building Modern PHP

{

parent::boot();

static::addGlobalScope(newActiveScope);

}

}

Page 306: Laravel: Up and Running: A Framework for Building Modern PHP
Page 307: Laravel: Up and Running: A Framework for Building Modern PHP

CONTACTWITHNONAMESPACEYoumayhavenoticedthatseveraloftheseexampleshaveusedtheclassContact,withnonamespace.Thisisabnormal,andI’veonlydonethistosavespaceinthebook.Normallyevenyourtop-levelmodelswouldliveatsomethinglikeApp\Contact.

RemovingglobalscopesTherearethreewaystoremoveaglobalscope,andallthreeusethewithoutGlobalScope()orwithoutGlobalScopes()methods.Ifyou’reremovingaclosure-basedscope,thefirstparameterofthatscope’saddGlobalScope()registrationwillbethekeyyouusedtoenableit:

$allContacts=Contact::withoutGlobalScope('active')->get();

Ifyou’reremovingasingleclass-basedglobalscope,youcanpasstheclassnametowithoutGlobalScope()orwithoutGlobalScopes():

Contact::withoutGlobalScope(ActiveScope::class)->get();

Contact::withoutGlobalScopes([ActiveScope::class,VipScope::class])->get();

Or,youcanjustdisableallglobalscopesforaquery:

Contact::withoutGlobalScopes()->get();

Page 308: Laravel: Up and Running: A Framework for Building Modern PHP

CustomizingFieldInteractionswithAccessors,Mutators,andAttributeCastingNowthatwe’vecoveredhowtogetrecordsintoandoutofthedatabasewithEloquent,let’stalkaboutdecoratingandmanipulatingtheindividualattributesonyourEloquentmodels.

Accessors,mutators,andattributecastingallallowyoutocustomizethewayindividualattributesofEloquentinstancesareinputoroutput.Withoutusinganyofthese,eachattributeofyourEloquentinstanceistreatedlikeastring,andyoucan’thaveanyattributesonyourmodelsthatdon’texistonthedatabase.Butwecanchangethat.

AccessorsAccessorsallowyoutodefinecustomattributesonyourEloquentmodelsforwhenyouarereadingdatafromthemodelinstance.Thismaybebecauseyouwanttochangehowaparticularcolumnisoutput,orbecauseyouwanttocreateacustomattributethatdoesn’texistinthedatabasetableatall.

Youdefineanaccessorbywritingamethodonyourmodelwiththefollowingstructure:get{PascalCasedPropertyName}Attribute.So,ifyourpropertynameisfirst_name,theaccessormethodwouldbenamedgetFirstNameAttribute.

Let’stryitout.First,we’lldecorateapreexistingcolumn(Example8-25).

Example8-25.DecoratingapreexistingcolumnwithEloquentaccessors//Modeldefinition:

classContactextendsModel

{

publicfunctiongetNameAttribute($value)

{

return$value?:'(Nonameprovided)';

}

}

//Accessorusage:

$name=$contact->name;

Butwecanalsouseaccessorstodefineattributesthatneverexistedinthedatabase,asseeninExample8-26.

Example8-26.DefininganattributewithnobackingcolumnusingEloquentaccessors//Modeldefinition:

classContactextendsModel

{

publicfunctiongetFullNameAttribute()

{

return$this->first_name.''.$this->last_name;

}

}

//Accessorusage:

$fullName=$contact->full_name;

MutatorsMutatorsworkthesamewayasaccessors,exceptthey’refordetermininghowtoprocess

Page 309: Laravel: Up and Running: A Framework for Building Modern PHP

settingthedatainsteadofgettingit.Justlikewithaccessors,youcanuseittomodifytheprocessofwritingdatatoexistingcolumns,ortoallowforsettingcolumnsthatdon’texistinthedatabase.

Youdefineamutatorbywritingamethodonyourmodelwiththefollowingstructure:set{PascalCasedPropertyName}Attribute.So,ifyourpropertynameisfirst_name,themutatormethodwouldbenamedsetFirstNameAttribute.

Let’stryitout.First,we’lladdaconstrainttoupdatingapreexistingcolumn(Example8-27).

Example8-27.DecoratingsettingthevalueofanattributewithEloquentmutators//Definingthemutator

classOrderextendsModel

{

publicfunctionsetAmountAttribute($value)

{

$this->attributes['amount']=$value>0?$value:0;

}

}

//Usingthemutator

$order->amount='15';

Thisrevealsthatthewaymutatorsareexpectedto“set”dataonthemodelisbysettingitin$this->attributeswiththecolumnnameasthekey.

Now,let’saddaproxycolumnforsetting,asshowninExample8-28.

Example8-28.AllowingforsettingthevalueofanonexistentattributewithEloquentmutators//Definingthemutator

classOrderextendsModel

{

publicfunctionsetWorkgroupNameAttribute($workgroupName)

{

$this->attributes['email']="{$workgroupName}@ourcompany.com";

}

}

//Usingthemutator

$order->workgroup_name='jstott';

Asyoucanprobablyguess,it’srelativelyuncommontocreateamutatorforanon-existentcolumn,becauseitcanbeconfusingtosetonepropertyandhaveitchangeadifferentcolumn—butitispossible.

AttributecastingYoucanprobablyimaginewritingaccessorstocastallofyourinteger-typefieldsasintegers,encodeanddecodeJSONtostoreinTEXTcolumn,orconvertTINYINT0and1toandfrombooleanvalues.

Thankfully,there’sasystemforthatinEloquentalready.It’scalledattributecasting,anditallowsyoutodefinethatanyofyourcolumnsshouldalwaysbetreated,bothonreadandonwrite,asiftheyareofaparticulardatatype.TheoptionsarelistedinTable8-1.

Table8-1.Possibleattributecastingcolumntypes

Page 310: Laravel: Up and Running: A Framework for Building Modern PHP

Table8-1.Possibleattributecastingcolumntypes

Type Description

int|integer CastswithPHP(int)

real|float|double CastswithPHP(float)

string CastswithPHP(string)

bool|boolean CastswithPHP(bool)

object Parsesto/fromJSON,asastdClassobject

array Parsesto/fromJSON,asanarray

collection Parsesto/fromJSON,asacollection

date|datetime ParsesfromdatabaseDATETIMEtoCarbon,andback

timestamp ParsesfromdatabaseTIMESTAMPtoCarbon,andback

Example8-29showshowyouuseattributecastinginyourmodel.

Example8-29.UsingattributecastingonanEloquentmodelclassContact

{

protected$casts=[

'vip'=>'boolean',

'children_names'=>'array',

'birthday'=>'date',

];

}

DatemutatorsYoucanchooseforparticularcolumnstobemutatedastimestampcolumnsbyaddingthemtothedatesarray,asseeninExample8-30.

Example8-30.DefiningcolumnstobemutatedastimestampsclassContact

{

protected$dates=[

'met_at'

];

}

Bydefault,thisarraycontainscreated_atandupdated_at,soaddingentriestodatesjustaddsthemtothelist.

However,there’snodifferencebetweenaddingcolumnstothislistandaddingthemto$this->castsastimestamp,sothisisbecomingabitofanunnecessaryfeaturenowthatattributecastingcancasttimestamps(newinLaravel5.2).

Page 311: Laravel: Up and Running: A Framework for Building Modern PHP

EloquentCollectionsWhenyoumakeanyquerycallinEloquentthathasthepotentialtoreturnmultiplerows,insteadofanarraythey’llcomepackagedinanEloquentcollection,whichisaspecializedtypeofcollection.Let’stakealookatcollectionsandEloquentcollections,andwhatmakesthembetterthanplainarrays.

IntroducingthebasecollectionLaravel’sCollectionobjects(Illuminate\Support\Collection)arealittlebitlikearraysonsteroids.Themethodstheyexposeonarray-likeobjectsaresohelpfulthat,onceyou’vebeenusingthemforawhile,you’lllikelywanttopullIlluminateintoevennon-Laravelprojectsjustforcollections—whichyoucan,withtheTightenco/Collectpackage.

Youcancreateacollectionbypassinganarrayintoitsconstructor,orbycreatinganemptycollectionandpushingentriesontoit.Laravelalsohasacollect()helper,whichisthesimplestwaytocreateacollection.Let’stryit:

$collection=collect([1,2,3]);

Nowlet’ssaywewanttofilteroutanyevennumbers:

$odds=$collection->reject(function($item){

return$item%2===0;

});

Orwhatifwewanttogetaversionofthearraywhereeachitemismultipliedby10?Wecandothatasfollows:

$multiplied=$collection->map(function($item){

return$item*10;

});

Wecanevengetonlytheevens,multiplythemallby10,andreducethemtoasinglenumberbysum:

$sum=$collection

->filter(function($item){

return$item%2==0;

})->map(function($item){

return$item*10;

})->sum();

Asyoucansee,collectionsprovideaseriesofmethods,whichcanoptionallybechained,toperformfunctionaloperationsonyourarrays.TheyprovidethesamefunctionalityasnativePHPmethodslikearray_map()andarray_reduce(),butyoudon’thavetomemorizePHP’sunpredictableparameterorder,andthemethodchainingsyntaxisendlesslymorereadable.

Therearemorethan60methodsavailableontheCollectionclass,includingmethodslike

Page 312: Laravel: Up and Running: A Framework for Building Modern PHP

max()andwhereIn(),flatten(),andflip(),andthere’snotenoughspacetocoverthemallhere.We’llcovermorein“Collections”,oryoucancheckouttheLaraveldocsonCollectionstoseeallofthemethods.

Page 313: Laravel: Up and Running: A Framework for Building Modern PHP

COLLECTIONSINTHEPLACEOFARRAYSCollectionscanalsobeusedinanycontext(excepttypehinting)whereyoucanusearrays;theyallowforiteration,soyoucanpassthemtoforeach,andtheyallowforarrayaccess,soifthey’rekeyedyoucantry$a=$collection['a'].

WhatEloquentcollectionsaddEachEloquentcollectionisanormalcollection,butextendedfortheparticularneedsofacollectionofEloquentresults.

Onceagain,there’snotenoughroomheretocoveralloftheadditions,butthey’recenteredaroundtheuniqueaspectsofinteractingwithacollectionnotjustofgenericobjects,butobjectsmeanttorepresentdatabaserows.

Forexample,everyEloquentcollectionhasamethodcalledmodelKeys()thatreturnsanarrayoftheprimarykeysofeveryinstanceinthecollection.find($id)looksforaninstancethathastheprimarykeyof$id.

Oneadditionalfeatureavailablehereistheabilitytodefinethatanygivenmodelshouldreturnitsresultswrappedinaspecificclassofcollection.So,ifyouwanttoaddspecificmethodstoanycollectionofobjectsoftheOrderclass—possiblyrelatedtosummarizingthefinancialdetailsofyourorders—youcouldcreateacustomOrderCollectionthatextendstheIlluminate\Database\Eloquent\Collectionclass,andthenregisteritinyourmodel,asshowninExample8-31.

Example8-31.CustomCollectionclassesforEloquentmodels...

classOrderCollectionextendsCollection

{

publicfunctionsumBillableAmount()

{

return$this->reduce(function($carry,$order){

return$carry+($order->billable?$order->amount:0);

},0);

}

}

...

classOrderextendsModel

{

publicfunctionnewCollection(array$models=[])

{

returnnewOrderCollection($models);

}

Now,anytimeyougetbackacollectionofOrders(e.g.,fromOrder::all())it’llactuallybeaninstanceoftheOrderCollectionclass:

$orders=Order::all();

$billableAmount=$orders->sumBillableAmount();

Page 314: Laravel: Up and Running: A Framework for Building Modern PHP

EloquentSerializationSerializationiswhathappenswhenyoutakesomethingcomplex—anarray,oranobject—andconvertittoastring.Inaweb-basedcontext,thatstringisoftenJSON,butitcouldtakeotherformsaswell.

Serializingcomplexdatabaserecordscanbe,well,complex,andthisisoneoftheplacesmanyORMsfallshort.Thankfully,yougettwopowerfulmethodsforfreewithEloquent:toArray()andtoJson().CollectionsalsohavetoArray()andtoJson(),soallofthesearevalid:

$contactArray=Contact::first()->toArray();

$contactJson=Contact::first()->toJson();

$contactsArray=Contact::all()->toArray();

$contactsJson=Contact::all()->toJson();

YoucanalsocastanEloquentinstanceorcollectiontoastring($string=(string)$contact;),butbothmodelsandcollectionswilljustruntoJson()andreturntheresult.

ReturningmodelsdirectlyfromroutemethodsLaravel’sroutereventuallyconvertseverythingroutesreturntoastring,sothere’saclevertrickyoucanuse.IfyoureturntheresultofanEloquentcallinacontroller,itwillbeautomaticallycasttoastring,andthereforereturnedasJSON.ThatmeansaJSON-returningroutecanbeassimpleaseitheroftheonesinExample8-32.

Example8-32.ReturningJSONfromroutesdirectly//routes/web.php

Route::get('api/contacts',function(){

returnContact::all();

});

Route::get('api/contacts/{id}',function($id){

returnContact::findOrFail($id);

});

HidingattributesfromJSONIt’sverycommontouseJSONreturnsinAPIs,andit’sverycommontowanttohidecertainattributesinthesecontexts,soEloquentmakesiteasytohideanyattributeseverytimeyoucasttoJSON.

Youcaneitherblacklistattributes,hidingtheonesyoulist:

classContactextendsModel

{

public$hidden=['password','remember_token'];

orwhitelistattributes,showingonlytheonesyoulist:

classContactextendsModel

{

Page 315: Laravel: Up and Running: A Framework for Building Modern PHP

public$visible=['name','email','status'];

Thisalsoworksforrelationships:

classUserextendsModel

{

public$hidden=['contacts'];

publicfunctioncontacts()

{

return$this->hasMany(Contact::class);

}

Page 316: Laravel: Up and Running: A Framework for Building Modern PHP

LOADINGTHECONTENTSOFARELATIONSHIPBydefault,thecontentsofarelationshiparenotloadedwhenyougetadatabaserecord,soitdoesn’tmatterwhetheryouhidethemornot.But,aswe’lllearnshortly,it’spossibletogetarecordwithitsrelateditems,andinthiscontext,thoseitemswillnotbeincludedinaserializedcopyofthatrecordifyouchoosetohidethatrelationship.

Incaseyou’recuriousnow,youcangetaUserwithallcontacts—assumingyou’vesetuptherelationshipcorrectly—withthefollowingcall:

$user=User::with('contacts')->first();

Theremightbetimeswhenyouwanttomakeanattributevisiblejustforasinglecall.That’spossible,withtheEloquentmethodmakeVisible():

$array=$user->makeVisible('remember_token')->toArray();

Page 317: Laravel: Up and Running: A Framework for Building Modern PHP
Page 318: Laravel: Up and Running: A Framework for Building Modern PHP

ADDINGAGENERATEDCOLUMNTOARRAYANDJSONOUTPUTIfyouhavecreatedanaccessorforacolumnthatdoesn’texist—forexample,ourfull_namecolumnfromExample8-26—addittothe$appendsarrayonthemodeltoaddittothearrayandJSONoutput:

classContactextendsModel

{

protected$appends=['full_name'];

publicfunctiongetFullNameAttribute()

{

return"{$this->first_name}{$this->last_name}";

Page 319: Laravel: Up and Running: A Framework for Building Modern PHP

EloquentRelationshipsInarelationaldatabasemodel,it’sexpectedthatyouwillhavetablesthatarerelatedtoeachother—hencethename.Eloquentprovidessimpleandpowerfultoolstomaketheprocessofrelatingyourdatabasetableseasierthaneverbefore.

Manyofourexamplesinthischapterhavebeencenteredaroundauserwhohasmanycontacts,arelativelycommonsituation.

InanORMlikeEloquent,youwouldcallthisaone-to-manyrelationship:theoneuserhasmanycontacts.

IfitwasaCRMwhereacontactcouldbeassignedtomanyusers,thenthiswouldbeamany-to-manyrelationship:manyuserscanberelatedtoonecontact,andeachusercanberelatedtomanycontacts.Auserhasandbelongstomanycontacts.

Ifeachcontactcanhavemanyphonenumbers,andauserwantedadatabaseofeveryphonenumberfortheirCRM,youwouldsaytheuserhasmanyphonenumbersthroughcontacts—thatis,auserhasmanycontacts,andthecontacthasmanyphonenumbers,sothecontactissortofanintermediary.

Andwhatifeachcontacthasanaddress,butyou’reonlyinterestedintrackingoneaddress?YoucouldhavealltheaddressfieldsontheContact,butyoumightalsocreateanAddressmodel—meaningthecontacthasoneaddress.

Finally,whatifyouwanttobeabletostar(favorite)contacts,butalsoevents?Thiswouldbeapolymorphicrelationship,whereauserhasmanystars,butsomemaybecontactsandsomemaybeevents.

So,let’sdigintohowtodefineandaccesstheserelationships.

OnetooneLet’sstartsimple:aContacthasonePhoneNumber.ThisrelationshipisdefinedinExample8-33.

Example8-33.Definingaone-to-onerelationshipclassContactextendsModel

{

publicfunctionphoneNumber()

{

return$this->hasOne(PhoneNumber::class);

}

Asyoucantell,themethodsdefiningrelationshipsareontheEloquentmodelitself($this->hasOne())andtake,atleastinthisinstance,thefullyqualifiedclassnameoftheclassthatyou’rerelatingthemto.

Howshouldthisbedefinedinyourdatabase?Sincewe’vedefinedthattheContacthasonePhoneNumber,EloquentexpectsthatthetablesupportingthePhoneNumberclass(likely

Page 320: Laravel: Up and Running: A Framework for Building Modern PHP

phone_numbers)hasacontact_idcolumnonit.Ifyounameditsomethingdifferent(forinstance,owner_id),you’llneedtochangeyourdefinition:

return$this->hasOne(PhoneNumber::class,'owner_id');

Here’showweaccessthephonenumberonacontact:

$contact=Contact::first();

$contactPhone=$contact->phoneNumber;

NoticethatwedefinethemethodinExample8-33withphoneNumber(),butweaccessitwith->phoneNumber.That’sthemagic.Youcouldalsoaccessitwith->phone_number.ThiswillreturnafullEloquentinstanceoftherelatedPhoneNumberrecord.

ButwhatifwewanttoaccesstheContactfromthePhoneNumber?There’samethodforthat,too(seeExample8-34).

Example8-34.Definingaone-to-onerelationship’sinverseclassPhoneNumberextendsModel

{

publicfunctioncontact()

{

return$this->belongsTo(Contact::class);

}

Thenweaccessitthesameway:

$contact=$phoneNumber->contact;

Page 321: Laravel: Up and Running: A Framework for Building Modern PHP

INSERTINGRELATEDITEMSEachrelationshiptypehasitsownquirksforhowtorelatemodels,buthere’sthecoreofhowitworks:passaninstancetosave(),oranarrayofinstancestosaveMany().Youcanalsopasspropertiestocreate()andit’llmakeanewinstanceforyou:

$contact=Contact::first();

$phoneNumber=newPhoneNumber;

$phoneNumber->number=8008675309;

$contact->phoneNumbers()->save($phoneNumber);

//or

$contact->phoneNumbers()->saveMany([

PhoneNumber::find(1),

PhoneNumber::find(2),

]);

//or

$contact->phoneNumbers()->create([

'number'=>'+13138675309'

]);

OnetomanyTheone-to-manyrelationshipisbyfarthemostcommon.Let’stakealookathowtodefinethatourUserhasmanyContacts(Example8-35).

Example8-35.Definingaone-to-manyrelationshipclassUserextendsModel

{

publicfunctioncontacts()

{

return$this->hasMany(Contact::class);

}

Onceagain,thisexpectsthattheContactmodel’sbackingtable(likelycontacts)hasauser_idcolumnonit.Ifitdoesn’t,overrideitbypassingthecorrectcolumnnameasthesecondparameterofhasMany().

Wecangetauser ’scontactsasfollows:

$user=User::first();

$usersContacts=$user->contacts;

Justlikewithonetoone,weusethenameoftherelationshipmethodandcallitasifitwereapropertyinsteadofamethod.However,thismethodreturnsacollectioninsteadofamodelinstance.AndthisisanormalEloquentcollection,soyoucanhaveallsortsoffunwithit:

$donors=$user->contacts->filter(function($contact){

return$contact->status=='donor';

});

Page 322: Laravel: Up and Running: A Framework for Building Modern PHP

$lifetimeValue=$contact->orders->reduce(function($carry,$order){

return$carry+$order->amount;

},0);

Justlikewithonetoone,wecanalsodefinetheinverse(Example8-36).

Example8-36.Definingaone-to-manyrelationship’sinverseclassContactextendsModel

{

publicfunctionuser()

{

return$this->belongsTo(User::class);

}

Andjustlikeonetoone,wecanaccesstheUserfromtheContact:

$userName=$contact->user->name;

Page 323: Laravel: Up and Running: A Framework for Building Modern PHP
Page 324: Laravel: Up and Running: A Framework for Building Modern PHP

ATTACHINGANDDETACHINGRELATEDITEMSFROMTHEATTACHEDITEM

Mostofthetimeweattachrelateditemsbyrunningsave()ontheparentandpassingintherelateditem,asin$user->contacts()->save($contact).Butifyouwanttoperformthebehaviorsontheattached(“child”)item,youcanuseassociate()anddissociate()onthemethodthatreturnsthebelongsTo():

$contact=Contact::first();

$contact->user()->associate(User::first());

$contact->save();

//andlater

$contact->user()->dissociate();

$contact->save();

UsingrelationshipsasquerybuildersUntilnow,we’vetakenthemethodname(e.g.,contacts())andcalleditasifwereaproperty(e.g.,$user->contacts).Whathappensifwecallitasamethod?Insteadofprocessingtherelationship,itwillreturnaprescopedquerybuilder.

SoifyouhaveUser1,andyoucallitscontacts()method,youwillnowhaveaquerybuilderprescopedto“allcontactsthathaveafielduser_idwiththevalueof1.”Youcanthenbuildoutafunctionalqueryfromthere:

$donors=$user->contacts()->where('status','donor')->get();

SelectingonlyrecordsthathavearelateditemYoucanalsochoosetoselectonlyrecordsthatmeetparticularcriteriawithregardtotheirrelateditemsusinghas():

$postsWithComments=Post::has('comments')->get();

Youcanalsoadjustthecriteriafurther:

$postsWithManyComments=Post::has('comments','>=',5)->get();

Youcannestthecriteria:

$usersWithPhoneBooks=User::has('contacts.phoneNumbers')->get();

Andfinally,youcanwritecustomqueriesontherelateditems:

//Getsallcontactswithaphonenumbercontainingthestring"867-5309"

$jennyIGotYourNumber=Contact::whereHas('phoneNumbers',function($query){

$query->where('number','like','%867-5309%');

Page 325: Laravel: Up and Running: A Framework for Building Modern PHP

});

Hasmanythrough“Hasmanythrough”isreallyaconveniencemethodforpullinginrelationshipsofarelationship.ThisistheexampleIgaveearlierwhereaUserhasmanyContactsandeachContacthasmanyPhoneNumbers.Whatifyouwanttogetauser ’slistofcontactphonenumbers?That’shasmanythrough.

Thisstructureassumesthatyourcontactstablehasauser_idtorelatethecontactstotheusers,andthephone_numberstablehasacontact_idtorelateittothecontacts.Then,wedefinetherelationshipontheUserasinExample8-37.

Example8-37.Definingahas-many-throughrelationshipclassUserextendsModel

{

publicfunctionphoneNumbers()

{

return$this->hasManyThrough(PhoneNumber::class,Contact::class);

}

You’daccessthisrelationshipusing$user->phone_numbers,andasalwaysyoucancustomizetherelationshipkeyontheintermediatemodel(withthethirdparameterofhasmanyThrough())andtherelationshipkeyonthedistantmodel(withthefourthparameter).

ManytomanyThisiswherethingsstarttogetcomplex.Let’stakeourexampleofaCRMthatallowsaUsertohavemanyContacts,andeachContacttoberelatedtomultipleusers.

First,wedefinetherelationshipontheUserasinExample8-38.

Example8-38.Definingamany-to-manyrelationshipclassUserextendsModel

{

publicfunctioncontacts()

{

return$this->belongsToMany(Contact::class);

}

}

Andsincethisismanytomany,theinverselooksexactlythesame(Example8-39).

Example8-39.Definingamany-to-manyrelationship’sinverseclassContactextendsModel

{

publicfunctionusers()

{

return$this->belongsToMany(User::class);

}

}

SinceasingleContactcan’thaveauser_idcolumnandasingleUsercan’thaveacontact_idcolumn,many-to-manyrelationshipsrelyonapivottablethatconnectsthetwo.The

Page 326: Laravel: Up and Running: A Framework for Building Modern PHP

conventionalnamingofthistableisdonebyplacingthetwosingulartablenamestogether,orderedalphabetically,andseparatingthembyanunderscore.

So,sincewe’relinkingusersandcontacts,ourpivottableshouldbenamedcontacts_users(ifyou’dliketocustomizethetablename,passitasthesecondparametertothebelongsToMany()methods).Itneedstwocolumns:contact_idanduser_id.

JustlikewithhasMany(),wegetaccesstoacollectionoftherelateditems,butthistimeit’sfrombothsides(Example8-40).

Example8-40.Accessingtherelateditemsfrombothsidesofamany-to-manyrelationship$user=User::first();

$user->contacts->each(function($contact){

//dosomething

});

$contact=Contact::first();

$contact->users->each(function($user){

//dosomething

});

$donors=$user->contacts()->where('status','donor')->get();

Page 327: Laravel: Up and Running: A Framework for Building Modern PHP

UNIQUEASPECTSOFATTACHINGANDDETACHINGMANY-TO-MANYRELATEDITEMS

Sinceyourpivottablecanhaveitsownproperties,youneedtobeabletosetthosepropertieswhenyou’reattachingamany-to-manyrelateditem.Youcandothatbypassinganarrayasthesecondparametertosave():

$user=User::first();

$contact=Contact::first();

$user->contacts()->save($contact,['status'=>'donor']);

Additionally,youcanuseattach()anddetach()and,insteadofpassinginaninstanceofarelateditem,youcanjustpassanID.Theyworkjustthesameassave(),butcanalsoacceptanarrayofIDswithoutyouneedingtorenamethemethodtosomethinglikeattachMany():

$user=User::first();

$user->contacts()->attach(1);

$user->contacts()->attach(2,['status'=>'donor']);

$user->contacts()->attach([1,2,3]);

$user->contacts()->attach([

1=>['status'=>'donor'],

2,

3

]);

$user->contacts()->detach(1);

$user->contacts()->detach([1,2]);

$user->contacts()->detach();//Detachesallcontacts

YoucanalsouseupdateExistingPivot()tomakechangesjusttothepivotrecord:

$user->contacts()->updateExistingPivot($contactId,[

'status'=>'inactive'

]);

Andifyou’dliketoreplacethecurrentrelationships,effectivelydetachingallpreviousrelationshipsandattachingnewones,youcanpassanarraytosync():

$user->contacts()->sync([1,2,3]);

$user->contacts()->sync([

1=>['status'=>'donor'],

2,

3

]);

GettingdatafromthepivottableOnethingthat’suniqueaboutmanytomanyisthatit’sourfirstrelationshipthathasapivottable.Thelessdatayouhaveonapivottable,thebetter,buttherearesomecaseswhereit’svaluabletostoreinformationonyourpivottable—forexample,youmightwanttostoreacreated_atfieldtoseewhenthisrelationshipwascreated.

Inordertostorethesefields,youhavetoaddthemtotherelationshipdefinition,likeinExample8-41.YoucandefinespecificfieldsusingwithPivot()oraddcreated_atandupdated_attimestampsusingwithTimestamps().

Page 328: Laravel: Up and Running: A Framework for Building Modern PHP

Example8-41.Addingfieldstoapivotrecordpublicfunctioncontacts()

{

return$this->belongsToMany(Contact::class)

->withTimestamps()

->withPivot('status','preferred_greeting');

}

Whenyougetamodelinstancethrougharelationship,itwillhaveapivotpropertyonit,whichwillrepresentitsplaceinthepivottableyoujustpulleditfrom.So,youcandosomethinglikeExample8-42.

Example8-42.Gettingdatafromarelateditem’spivotentry$user=User::first();

$user->contacts->each(function($contact){

echosprintf(

'Contactassociatedwiththisuserat:%s',

$contact->pivot->created_at

);

});

PolymorphicRemember,ourpolymorphicrelationshipiswherewehavemultipleEloquentclassescorrespondingtothesamerelationship.We’regoingtouseStars(likefavorites)rightnow.AusercanstarbothContactsandEvents,andthat’swherethenamepolymorphiccomesfrom:asingleinterfacetoobjectsofmultipletypes.

So,we’llneedthreetables,andthreemodels:Star,Contact,andEvent(and,ofcourse,User,butwe’llgetthereinasecond).Thecontactsandeventstableswilljustbeastheynormallyare,andthestarstablewillcontainanidfield,astarrable_id,andastarrable_type.ForeachStar,you’llbedefiningwhich“type”(e.g.,ContactorEvent)andwhichIDofthattype(e.g.,1)itis.

Let’screateourmodels,asseeninExample8-43.

Example8-43.CreatingthemodelsforapolymorphicstarringsystemclassStarextendsModel

{

publicfunctionstarrable()

{

return$this->morphsTo();

}

}

classContactextendsModel

{

publicfunctionstars()

{

return$this->morphMany(Star::class,'starrable');

}

}

classEventextendsModel

{

publicfunctionstars()

{

Page 329: Laravel: Up and Running: A Framework for Building Modern PHP

return$this->morphMany(Star::class,'starrable');

}

}

So,howdowecreateaStar?

$contact=Contact::first();

$contact->stars()->create();

It’sthateasy.TheContactisnowstarred.

InordertofindalloftheStarsonagivenContact,wecallthestars()methodlikeinExample8-44.

Example8-44.Retrievingtheinstancesofapolymorphicrelationship$contact=Contact::first();

$contact->stars->each(function($star){

//Dostuff

});

IfwehaveaninstanceofStar,wecangetitstargetbycallingthemethodweusedtodefineitsmorphTo(),whichinthiscontextisstarrable().TakealookatExample8-45.

Example8-45.Retrievingthetargetofpolymorphicinstance$stars=Star::all();

$stars->each(function($star){

var_dump($star->starrable);//AninstanceofContactorEvent

});

Finally,youmightbewondering,“WhatifIcarewhostarredthiscontact?”That’sagreatquestion;ofcourseyoudo.It’sassimpleasaddinguser_idtoyourstarstable,andthensettingupthataUserhasmanyStarsandaStarbelongsto_oneUser—aone-to-manyrelationship(Example8-46).ThestarstablebecomesalmostapivottablebetweenyourUserandyourContactsandEvents.

Example8-46.ExtendingapolymorphicsystemtodifferentiatebyuserclassStarextendsModel

{

publicfunctionstarrable()

{

return$this->morphsTo;

}

publicfunctionuser()

{

return$this->belongsTo(User::class);

}

}

classUserextendsModel

{

publicfunctionstars()

{

return$this->hasMany(Star::class);

}

}

Page 330: Laravel: Up and Running: A Framework for Building Modern PHP

That’sit!Youcannowrun$star->useror$user->starstofindalistofaUser’sStarsortofindthestarringUserfromaStar.Also,whenyoucreateanewStar,you’llnowwanttopasstheUser:

$user=User::first();

$event=Event::first();

$event->stars()->create(['user_id'=>$user->id]);

ManytomanypolymorphicThemostcomplexandleastcommonoftherelationshiptypes,many-to-manypolymorphicrelationshipsarelikepolymorphicrelationships,exceptinsteadofbeingonetomanythey’remanytomany.

Themostcommonexampleforthisrelationshiptypeisthetag,soI’llkeepitsafeandusethatasourexample.Let’simagineyouwanttobeabletotagContactsandEvents.Theuniquenessofmany-to-manypolymorphismisthatit’smanytomany:eachtagmaybeappliedtomultipleitems,andeachtaggeditemmighthavemultipletags.Andtoaddtothat,it’spolymorphic:tagscanberelatedtoitemsofseveraldifferenttypes.Forthedatabase,we’llstartwiththenormalstructureofthepolymorphicrelationshipbutalsoaddapivottable.

Thismeanswe’llneedacontactstable,aneventstable,andatagstable,allshapedlikenormalwithanIDandwhateverpropertiesyouwant,andanewtaggablestable,whichwillhaveatag_id,ataggable_id,andataggable_type.Eachentryintothetaggablestablewillrelateatagwithoneofthetaggablecontenttypes.

Nowlet’sdefinethisrelationshiponourmodels,asseeninExample8-47.

Example8-47.Definingapolymorphicmany-to-manyrelationshipclassContactextendsModel

{

publicfunctiontags()

{

return$this->morphToMany(Tag::class,'taggable');

}

}

classEventextendsModel

{

publicfunctiontags()

{

return$this->morphToMany(Tag::class,'taggable');

}

}

classTagextendsModel

{

publicfunctioncontacts()

{

return$this->morphedByMany(Contact::class,'taggable');

}

publicfunctionevents()

{

return$this->morphedByMany(Event::class,'taggable');

}

}

Page 331: Laravel: Up and Running: A Framework for Building Modern PHP

Here’showtocreateyourfirsttag:

$tag=Tag::firstOrCreate(['name'=>'likes-cheese']);

$contact=Contact::first();

$contact->tags()->attach($tag->id);

Wegettheresultsofthisrelationshiplikenormal,asseeninExample8-48.

Example8-48.Accessingtherelateditemsfrombothsidesofamany-to-manypolymorphicrelationship$contact=Contact::first();

$contact->tags->each(function($tag){

//Dostuff

});

$tag=Tag::first();

$tag->contacts->each(function($contact){

//Dostuff

});

Page 332: Laravel: Up and Running: A Framework for Building Modern PHP

ChildRecordsUpdatingParentRecordTimestampsRemember,anyEloquentmodelsbydefaultwillhavecreated_atandupdated_attimestamps.Eloquentwillsettheupdated_attimestampautomaticallyanytimeyoumakeanychangestoarecord.

WhenarelateditemeitherbelongsTo()orbelongsToMany()anotheritem,itmightbevaluabletomarktheotheritemasupdatedanytimetherelateditemisupdated.Forexample,ifaPhoneNumberisupdated,theContactit’sconnectedtoshouldbemarkedashavingbeenupdatedaswell.

Wecanaccomplishthisbyaddingthemethodnameforthatrelationshiptoa$touchesarraypropertyonthechildclass,asinExample8-49.

Example8-49.UpdatingaparentrecordanytimethechildrecordisupdatedclassPhoneNumberextendsModel

{

protected$touches=['contact'];

publicfunctioncontact()

{

return$this->belongsTo(Contact::class);

}

EagerloadingBydefault,Eloquentloadsrelationshipsusing“lazyloading.”Thismeanswhenyoufirstloadamodelinstance,itsrelatedmodelswillnotbeloadedalongwithit.Rather,they’llonlybeloadedifyoumakeaseparatecalltopullthemin;they’re“lazy”anddon’tdoanyworkuntilcalledupon.

Thiscanbecomeaproblemifyou’reiteratingoveralistofmodelinstancesandeachhasarelateditem(oritems)thatyou’reworkingon.Theproblemwithlazyloadingisthatitcanintroducesignificantdatabaseload(oftentheN+1problem,ifyou’refamiliarwiththeterm;ifnot,justignorethisparentheticalremark).Forinstance,everytimetheloopinExample8-50runs,itexecutesanewdatabasequerytolookupthePhoneNumberforthatContact.

Example8-50.$contacts=Contact::all();

foreach($contactsas$contact){

foreach($contact->phone_numbersas$phone_number){

echo$phone_number->number;

}

}

Ifyouareloadingamodelinstanceandyouknowyou’llbeworkingwithitsrelationships,youcaninsteadchooseto“eager-load”oneormanyofitssetsofrelateditems:

$contacts=Contact::with('phoneNumbers')->get();

Usingthewith()methodwitharetrievalgetsalloftheitemsrelatedtothepulleditem(s),and

Page 333: Laravel: Up and Running: A Framework for Building Modern PHP

asyoucanseeinthisexample,youpassitthenameofthemethodtherelationshipisdefinedby.

Whenweuseeagerloading,insteadofpullingtherelateditemsoneatatimewhenthey’rerequested(selectingonephonenumbereachtimeaforeachloopruns),wehaveasinglequerytopulltheinitialitems(selectingallcontacts)andasecondquerytopullalltheirrelateditems(selectingallphonenumbersownedbythecontactswejustpulled).

Youcaneager-loadmultiplerelationshipsbypassingmultipleparameterstothewith()call:

$contacts=Contact::with('phoneNumbers','addresses')->get();

Andyoucannesteagerloadingtoeager-loadtherelationshipsofrelationships:

$authors=Author::with('posts.comments')->get();

ConstrainingeagerloadsIfyouwanttoeager-loadarelationship,butnotalloftheitems,youcanpassaclosuretowith()todefineexactlywhichrelateditemstoeager-load:

$contacts=Contact::with(['addresses'=>function($query){

$query->where('mailable',true);

}])->get();

LazyeagerloadingIknowitsoundscrazy,becausewejustdefinedeagerloadingassortoftheoppositeoflazyloading,butsometimesyoudon’tknowyouwanttoperformaneager-loadqueryuntilaftertheinitialinstanceshavebeenpulled.Youcanstillperformaneagerloadafterthefact,withlazyeagerloading:

$contacts=Contact::all();

if($showPhoneNumbers){

$contacts->load('phoneNumbers');

}

EagerloadingonlythecountIfyouwanttoeager-loadrelationshipsbutonlysoyoucanhaveaccesstothecountofitemsineachrelationship,youcantrywithCount():

$authors=Author::withCount('posts')->get();

//addsa"posts_count"integertoeachAuthorwithacountofthat

//Author'snumberofrelatedposts

Page 334: Laravel: Up and Running: A Framework for Building Modern PHP

EloquentEventsEloquentmodelsfireeventsoutintothevoidofyourapplicationeverytimecertainactionshappen,regardlessofwhetheryou’relistening.Ifyou’refamiliarwithpub/sub,it’sthissamemodel(andyoucanlearnmoreaboutLaravel’sentireeventsysteminChapter16).

Here’saquickrundownofbindingalistenertowhenanewContactiscreated.We’regoingtobinditintheboot()methodofAppServiceProvider,andlet’simaginewe’renotifyingathird-partyserviceeverytimewecreateanewContact.

Example8-51.BindingalistenertoanEloquenteventclassAppServiceProviderextendsServiceProvider

{

publicfunctionboot()

{

$thirdPartyService=newSomeThirdPartyService;

Contact::creating(function($contact)use($thirdPartyService){

try{

$thirdPartyService->addContact($contact);

}catch(Exception$e){

Log::error('FailedaddingcontacttoThirdPartyService;cancelled.');

returnfalse;

}

});

}

WecanseeafewthingsinExample8-51.First,weuseModelname::eventName()asthemethod,andpassitaclosure.Theclosuregetsaccesstothemodelinstancethatisbeingoperatedon.Second,we’regoingtoneedtodefinethislistenerinaserviceprovidersomewhere.Andthird,ifwereturnfalse,theoperationwillcancelandthesave()orupdate()willbecancelled.

HerearetheeventsthateveryEloquentmodelfires:creating

created

updating

updated

saving

saved

deleting

deleted

Page 335: Laravel: Up and Running: A Framework for Building Modern PHP

restoring

restored

Mostoftheseshouldbeprettyclear,exceptpossiblyrestoringandrestored,whichfirewhenyou’rerestoringasoft-deletedrow.Also,savingisfiredforbothcreatingandupdatingandsavedisfiredforbothcreatedandupdated.

Page 336: Laravel: Up and Running: A Framework for Building Modern PHP

TestingLaravel’sentireapplicationtestingframeworkmakesiteasytotestyourdatabase—notbywritingunittestsagainstEloquent,butbyjustbeingwillingtotestyourentireapplication.

Takethisscenario.Youwanttotesttoensurethataparticularpageshowsonecontactbutnotanother.SomeofthatlogichastodowiththeinterplaybetweentheURLandthecontrollerandthedatabase,sothebestwaytotestitisanapplicationtest.YoumightbethinkingaboutmockingEloquentcallsandtryingtoavoidthesystemhittingthedatabase.Don’tdoit.TryExample8-52instead.

Example8-52.Testingdatabaseinteractionswithsimpleapplicationtestspublicfunctiontest_active_page_shows_active_and_not_inactive_contacts()

{

$activeContact=factory(Contact::class,'active')->create();

$inactiveContact=factory(Contact::class,'inactive')->create();

$this->visit('active-contacts')

->see($activeContact->name)

->dontSee($inactiveContact->name);

}

Asyoucansee,modelfactoriesandLaravel’sapplicationtestingfeaturesaregreatfortestingdatabasecalls.

Alternatively,youcanlookforthatrecorddirectlyinthedatabase,asinExample8-53.

Example8-53.UsingseeInDatabase()tocheckforcertainrecordsinthedatabasepublicfunctiontest_contact_creation_works()

{

$this->post('contacts',[

'email'=>'[email protected]'

]);

$this->seeInDatabase('contacts',[

'email'=>'[email protected]'

]);

}

EloquentandLaravel’sdatabaseframeworkaretestedextensively.Youdon’tneedtotestthem.Youdon’tneedtomockthem.Ifyoureallywanttoavoidhittingthedatabase,youcanusearepositoryandthenreturnunsavedinstancesofyourEloquentmodels.Butthemostimportantmessageis,testthewayyourapplicationusesyourdatabaselogic.

Ifyouhavecustomaccessors,mutators,scopes,orwhateverelse,youcanalsotestthemdirectly,asinExample8-54.

Example8-54.Testingaccessors,mutators,andscopespublicfunctiontest_full_name_accessor_works()

{

$contact=factory(Contact::class)->make([

'first_name'=>'Alphonse',

'last_name'=>'Cumberbund'

]);

$this->assertEquals('AlphonseCumberbund',$contact->fullName);

}

Page 337: Laravel: Up and Running: A Framework for Building Modern PHP

publicfunctiontest_vip_scope_filters_out_non_vips()

{

$vip=factory(Contact::class,'vip')->create();

$nonVip=factory(Contact::class)->create();

$vips=Contact::vips()->get();

$this->assertTrue($vips->contains(['id'=>$vip->id]));

$this->assertFalse($vips->contains(['id'=>$nonVip->id]));

}

Justavoidwritingteststhatleaveyoucreatingcomplex“Demeterchains”toassertthataparticularfluentstackwascalledonsomedatabasemock.Ifyourtestingstartstogetoverwhelmingandcomplexaroundthedatabaselayer,it’sbecauseyou’reallowingpreconceivednotionstoforceyouintounnecessarilycomplexsystems.Keepitsimple.

Page 338: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRLaravelcomeswithasuiteofpowerfuldatabasetools,includingmigrations,seeding,anelegantquerybuilder,andEloquent,apowerfulActiveRecordORM.Laravel’sdatabasetoolsdon’trequireyoutouseEloquentatall—youcanaccessandmanipulatethedatabasewithathinlayerofconveniencewithouthavingtowriteSQLdirectly.ButaddinganORM,whetherit’sEloquentorDoctrineorwhateverelse,iseasyandcanworkneatlywithLaravel’scoredatabasetools.

EloquentfollowstheActiveRecordpattern,whichmakesitsimpletodefineaclassofdatabase-backedobjects,includingwhichtablethey’restoredin,theshapeoftheircolumns,accessorsandmutators,andmuchmore.EloquentcanhandleeverysortofnormalSQLactionandalsocomplexrelationships,uptoandincludingpolymorphicmany-to-manyrelationships.

Laravelalsohasarobustsystemfortestingdatabases,includingmodelfactories.

Page 339: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter9.UserAuthenticationandAuthorization

Settingupabasicuserauthenticationsystem—includingregistration,login,sessions,passwordresets,andaccesspermissions—canoftenbeoneofthemoretime-consumingpiecesofcreatingthefoundationofanapplication.It’saprimecandidateforextractingfunctionalityouttoalibrary,andtherearequiteafewsuchlibraries.

Butbecauseofhowmuchauthenticationneedsvaryacrossprojects,mostauthenticationsystemsgrowbulkyandunusablequickly.Thankfully,Laravelhasfoundawaytomakeanauthenticationsystemthat’seasytouseandunderstand,butflexibleenoughtofitinavarietyofsettings.

EverynewinstallofLaravelhasacreate_users_tablemigrationandaUsermodelbuiltinoutofthebox.LaraveloffersanArtisanmake:authcommandthatseedsacollectionofauthentication-relatedviewsandroutes.AndeveryinstallcomeswithaRegisterController,aLoginController,aForgotPasswordController,andaResetPasswordController.TheAPIsarecleanandclear,andtheconventionsallworktogethertoprovideasimple—andseamless—authenticationandauthorizationsystem.

Page 340: Laravel: Up and Running: A Framework for Building Modern PHP
Page 341: Laravel: Up and Running: A Framework for Building Modern PHP

DIFFERENCESINAUTHSTRUCTUREINLARAVEL5.3NotethatinLaravel5.1and5.2,mostofthisfunctionalitylivedintheAuthController;in5.3,thisfunctionalityhasbeensplitoutintomultiplecontrollers.Manyofthespecificswe’llcoverhereabouthowtocustomizeredirectroutes,authguards,andsucharedifferentin5.1and5.2(thoughallthecorefunctionalityisthesame).So,ifyou’reon5.1or5.2andwanttochangesomeofthedefaultauthenticationbehaviors,you’lllikelyneedtodigabitintoyourAuthControllertoseehowexactlyyoushouldcustomizeit.

Page 342: Laravel: Up and Running: A Framework for Building Modern PHP

TheUserModelandMigrationWhenyoucreateanewLaravelapplication,thefirstmigrationandmodelyou’llseearethecreate_users_tablemigrationandtheApp\Usermodel.Example9-1shows,straightfromthemigration,thefieldsyou’llgetinyouruserstable.

Example9-1.Laravel’sdefaultusermigrationSchema::create('users',function(Blueprint$table){

$table->increments('id');

$table->string('name');

$table->string('email')->unique();

$table->string('password');

$table->rememberToken();

$table->timestamps();

});

WehaveanautoincrementingprimarykeyID,aname,auniqueemail,apassword,a“rememberme”token,andcreatedandmodifiedtimestamps.Thiscoverseverythingyouneedtohandlebasicuserauthenticationinmostapps.

Page 343: Laravel: Up and Running: A Framework for Building Modern PHP
Page 344: Laravel: Up and Running: A Framework for Building Modern PHP

THEDIFFERENCEBETWEENAUTHENTICATIONANDAUTHORIZATION

Authenticationmeansverifyingwhosomeoneis,andallowingthemtoactasthatpersoninyoursystem.Thisincludestheloginandlogoutprocesses,andanytoolsthatallowtheuserstoidentifythemselvesduringtheirtimeusingtheapplication.

Authorizationmeansdeterminingwhethertheauthenticateduserisallowed(authorized)toperformaspecificbehavior.Forexample,anauthorizationsystemallowsustoforbidanynonadministratorsfromviewingthesite’searnings.

TheUsermodelisabitmorecomplex,asyoucanseeinExample9-2.TheApp\Userclassitselfissimple,butitextendstheIlluminate\Foundation\Auth\Userclass,whichpullsinseveraltraits.

Example9-2.Laravel’sdefaultUsermodel<?php

//App\User

namespaceApp;

useIlluminate\Notifications\Notifiable;

useIlluminate\Foundation\Auth\UserasAuthenticatable;

classUserextendsAuthenticatable

{

useNotifiable;

/**

*Theattributesthataremassassignable.

*

*@vararray

*/

protected$fillable=[

'name','email','password',

];

/**

*Theattributesexcludedfromthemodel'sJSONform.

*

*@vararray

*/

protected$hidden=[

'password','remember_token',

];

}

<?php

//Illuminate\Foundation\Auth\User

namespaceIlluminate\Foundation\Auth;

useIlluminate\Auth\Authenticatable;

useIlluminate\Database\Eloquent\Model;

useIlluminate\Auth\Passwords\CanResetPassword;

useIlluminate\Foundation\Auth\Access\Authorizable;

useIlluminate\Contracts\Auth\AuthenticatableasAuthenticatableContract;

useIlluminate\Contracts\Auth\Access\AuthorizableasAuthorizableContract;

useIlluminate\Contracts\Auth\CanResetPasswordasCanResetPasswordContract;

classUserextendsModelimplements

AuthenticatableContract,

AuthorizableContract,

CanResetPasswordContract

{

Page 345: Laravel: Up and Running: A Framework for Building Modern PHP

useAuthenticatable,Authorizable,CanResetPassword;

}

Page 346: Laravel: Up and Running: A Framework for Building Modern PHP

ELOQUENTMODELREFRESHERIfthisisentirelyunfamiliar,considerreadingChapter8beforecontinuingtolearnhowEloquentmodelswork.

So,whatcanwelearnfromthismodel?First,usersliveintheuserstable;Laravelwillinferthisfromtheclassname.Weareabletofilloutthename,email,andpasswordpropertieswhencreatinganewuser,andthepasswordandremember_tokenpropertiesareexcludedwhenoutputtingtheuserasJSON.Lookinggoodsofar.

WealsocanseefromthecontractsandthetraitsintheIlluminate\Foundation\AuthverisonofUserthattherearesomefeaturesintheframework(theabilitytoauthenticate,toauthorize,andtoresetpasswords)thattheoreticallycouldbeappliedtoothermodels,notjusttheUsermodel,andthatcouldbeappliedindividuallyortogether.

CONTRACTSANDINTERFACES

YoumayhavenoticedthatsometimesIwritetheword“contract”andsometimes“interface,”andthatalmostalloftheinterfacesinLaravelareundertheContractsnamespace.

APHPinterfaceisessentiallyanagreementbetweentwoclassesthatoneoftheclasseswill“behave”acertainway.It’sabitlikeacontractbetweenthem,andthinkingaboutitasacontractgivesabitmoreinherentmeaningtothenamethancallingitaninterfacedoes.

Intheend,though,they’rethesamething:anagreementthataclasswillprovidecertainmethodswithacertainsignature.

Onarelatednote,theIlluminate\ContractsnamespacecontainsagroupofinterfacesthatLaravelcomponentsimplementandtypehint.ThismakesiteasytodevelopsimilarcomponentsthatimplementthesameinterfacesandswapthemintoyourapplicationinplaceofthestockIlluminatecomponents.WhentheLaravelcoreandcomponentstypehintamailer,forexample,theydon’ttypehinttheMailerclass.Instead,theytypehinttheMailercontract(interface),makingiteasytoprovideyourownmailer.Tolearnmoreabouthowtodothis,takealookatChapter11.

TheAuthenticatablecontractrequiresmethods(getAuthIdentifier(),etc.)thatallowtheframeworktoauthenticateinstancesofthismodeltotheauthsystem;theAuthenticatabletraitincludesthemethodsnecessarytosatisfythatcontractwithanaverageEloquentmodel.

TheAuthorizablecontractrequiresmethods(can(),cannot())thatallowtheframeworktoauthorizeinstancesofthismodelfortheiraccesspermissionsindifferentcontexts.Unsurprisingly,theAuthorizabletraitprovidesmethodsthatwillsatisfytheAuthorizablecontractforanaverageEloquentmodel.

Andfinally,theCanResetPasswordcontractrequiresonemethod(getEmailForPasswordReset())thatallowstheframeworkto,youguessedit,resetthepasswordofanyentitythatsatisfiesthiscontract.ThetraitprovidesthatmethodforanaverageEloquentmodel.

Atthispoint,wehavetheabilitytoeasilyrepresentanindividualuserinthedatabase(withthemigration),andtopullthemoutwithamodelinstancethatcanbeauthenticated(loggedinand

Page 347: Laravel: Up and Running: A Framework for Building Modern PHP

out),authorized(checkedforaccesspermissionstoaparticularresource),andsentapasswordresetemail.

Page 348: Laravel: Up and Running: A Framework for Building Modern PHP

Usingtheauth()GlobalHelperandtheAuthFacadeTheauth()globalhelperistheeasiestwaytointeractwiththestatusoftheauthenticateduserthroughoutyourapp.YoucanalsoinjectaninstanceofIlluminate\Auth\AuthManagerandgetthesamefunctionality,orusetheAuthfacade.

Themostcommonusagesaretocheckwhetherauserisloggedin(auth()->check()returnstrueifthecurrentuserisloggedin;auth()->guest()returnstrueiftheuserisnotloggedin)andtogetthecurrentlylogged-inuser(useauth()->user(),orauth()->id()forjusttheID;bothreturnnullifnouserisloggedin).

TakealookatExample9-3forasampleusageoftheglobalhelperinacontroller.

Example9-3.Sampleusageoftheauth()globalhelperinacontrollerpublicfunctiondashboard()

{

if(auth()->guest()){

returnredirect('sign-up');

}

returnview('dashboard')

->with('user',auth()->user());

}

Page 349: Laravel: Up and Running: A Framework for Building Modern PHP

TheAuthControllersSo,howdoweactuallylogusersin?Andhowdowetriggerthosepasswordresets?

ItallhappensintheAuth-namespacedcontrollers:RegisterController,LoginController,ResetPasswordController,andForgotPasswordController.

Page 350: Laravel: Up and Running: A Framework for Building Modern PHP

RegisterControllerTheRegisterController,incombinationwiththeRegistersUserstrait,containssensibledefaultsforhowtoshownewusersaregistrationform,howtovalidatetheirinput,howtocreatenewusersoncetheirinputisvalidated,andwheretoredirectthemafterward.

Thecontrolleritselfjustcontainsafewhooksthatthetraitswillcallatgivenpoints.Thatmakesiteasytocustomizeafewcommonbehaviorswithouthavingtodigdeeplyintothecodethatmakesitallwork.

The$redirectTopropertydefineswhereuserswillberedirectedafterregistration.Thevalidator()methoddefineshowtovalidateregistrations.Andthecreate()methoddefineshowtocreateanewuserbasedonanincomingregistration.TakealookatExample9-4toseethedefaultRegisterController.

Example9-4.Laravel’sdefaultRegisterController...

classRegisterControllerextendsController

{

useRegistersUsers;

protected$redirectTo='/home';

...

protectedfunctionvalidator(array$data)

{

returnValidator::make($data,[

'name'=>'required|max:255',

'email'=>'required|email|max:255|unique:users',

'password'=>'required|min:6|confirmed',

]);

}

protectedfunctioncreate(array$data)

{

returnUser::create([

'name'=>$data['name'],

'email'=>$data['email'],

'password'=>bcrypt($data['password']),

]);

}

}

RegistersUserstraitTheRegistersUserstrait,whichtheRegisterControllerimports,handlesafewprimaryfunctionsfortheregistrationprocess.First,itshowsuserstheregistrationformview,withtheshowRegistrationForm()method.Ifyouwantnewuserstoregisterwithaviewotherthanauth.registeryoucanoverridetheshowRegistrationForm()methodinyourRegisterController.

Next,ithandlesthePOSToftheregistrationformwiththeregister()method.Thismethodpassestheuser ’sregistrationinputtothevalidatorfromthevalidator()methodofyourRegisterController,andthenontothecreate()method.

Page 351: Laravel: Up and Running: A Framework for Building Modern PHP

Andfinally,theredirectPath()method(pulledinviatheRedirectsUserstrait)defineswhereusersshouldberedirectedafterasuccessfulregistration.YoucandefinethisURIwiththeredirectTopropertyonyourcontroller,oryoucanoverridetheredirectPath()methodandreturnwhateveryouwant.

Ifyouwantthistraittouseadifferentauthguardthanthedefault(you’lllearnmoreaboutguardsin“Guards”),youcanoverridetheauth()methodandhaveitreturnwhicheverguardyou’dlike.

Page 352: Laravel: Up and Running: A Framework for Building Modern PHP

LoginControllerTheLoginController,unsurprisingly,allowstheusertologin.ItbringsintheAuthenticatesUserstrait,whichbringsintheRedirectsUsersandThrottlesLoginstraits.

LiketheRegistrationController,theLoginControllerhasa$redirectTopropertythatallowsyoutocustomizethepaththeuserwillberedirectedtoafterasuccessfullogin.EverythingelselivesbehindtheAuthenticatesUserstrait.

AuthenticatesUserstraitTheAuthenticatesUserstraitisresponsibleforshowinguserstheloginform,validatingtheirlogins,throttlingfailedlogins,handlinglogouts,andredirectingusersafterasuccessfullogin.

TheshowLoginForm()methoddefaultstoshowingtheusertheauth.loginview,butyoucanoverrideitifyou’dlikeittouseadifferentview.

Thelogin()methodacceptsthePOSTfromtheloginform.ItvalidatestherequestusingthevalidateLogin()method,whichyoucanoverrideifyou’dliketocustomizethevalidation.ItthenhooksintothefunctionalityoftheThrottlesLoginstrait,whichwe’llcovershortly,torejectuserswithtoomanyfailedlogins.Andfinally,itredirectstheuser,eithertoherintendedpath(iftheuserwasredirectedtotheloginpagewhenattemptingtovisitapagewithintheapp)ortothepathdefinedbytheredirectPath()method,whichreturnsyour$redirectToproperty.

Thetraitcallstheemptyauthenticated()methodafterasuccessfullogin,soifyou’dliketoperformanysortofbehaviorinresponsetoasuccessfullogin,justoverridethismethodinyourLoginController.

There’sausername()methodthatdefineswhichofyouruserscolumnsisthe“username”;thisdefaultstoemailbutyoucanchangethatbyoverwritingtheusername()methodinyourcontrollertoreturnthenameofyourusernamecolumn.

And,likeintheRegistersUserstrait,youcanoverridetheguard()methodtodefinewhichauthguard(moreonthatin“Guards”)thiscontrollershoulduse.

ThrottlesLoginstraitTheThrottlesLoginstraitisaninterfacetoLaravel’sIlluminate\Cache\RateLimiterclass,whichisautilitytorate-limitanyeventusingthecache.Thistraitappliesratelimitingtouserlogins,limitingusersfromusingtheloginformifthey’vehadtoomanyfailedloginswithinacertainamountoftime.ThisfunctionalitydoesnotexistinLaravel5.1.

IfyouimporttheThrottlesLoginstrait,allofitsmethodsareprotected,whichmeansthey

Page 353: Laravel: Up and Running: A Framework for Building Modern PHP

can’tactuallybeaccessedasroutes.Instead,theAuthenticatesUserstraitlookstoseewhetheryou’veimportedtheThrottlesLoginstrait,andifso,it’llattachitsfunctionalitytoyourloginswithoutanyworkonyourpart.SincethedefaultLoginControllerimportsboth,you’llgetthisfunctionalityforfreeifyouusetheauthscaffold.

ThrottlesLoginslimitsanygivencombinationofusernameandIPaddressto5attemptsper60seconds.Usingthecache,itincrementsthe“failedlogin”countofagivenusername/IPaddresscombination,andifanyuserreaches5failedloginattemptswithin60seconds,itredirectsthatuserbacktotheloginpagewithanappropriateerroruntilthe60secondsisover.

Page 354: Laravel: Up and Running: A Framework for Building Modern PHP

ResetPasswordControllerTheResetPasswordControllersimplypullsintheResetsPasswordstrait.Thistraitprovidesvalidationandaccesstobasicpasswordresetviews,andthenusesaninstanceofLaravel’sPasswordBrokerclass(oranythingelseimplementingthePasswordBrokerinterface,ifyouchoosetowriteyourown)tohandlesendingpasswordresetemailsandactuallyresettingthepasswords.

Justliketheothertraitswe’vecovered,ithandlesshowingtheresetpasswordview(showResetForm()showstheauth.passwords.resetview),andthePOSTrequestthatissentfromthatview(reset()validatesandsendstheappropriateresponse).TheresetPassword()methodactuallyresetsthepassword,andyoucancustomizethebrokerwithbroker()andtheauthguardwithguard().

Ifyou’reinterestedincustomizinganyofthisbehavior,justoverridethespecificmethodyouwanttocustomizeinyourcontroller.

Page 355: Laravel: Up and Running: A Framework for Building Modern PHP

ForgotPasswordControllerTheForgotPasswordControllersimplypullsintheSendsPasswordResetEmailstrait.Itshowstheauth.passwords.emailformwiththeshowLinkRequestForm()method,andhandlesthePOSTofthatformwiththesendResetLinkEmail()method.Youcancustomizethebrokerwiththebroker()method.

Page 356: Laravel: Up and Running: A Framework for Building Modern PHP

Auth::routes()Nowthatwehavetheauthcontrollersprovidingsomemethodsforaseriesofpre-definedroutes,we’llwantouruserstoactuallybeabletohitthoseroutes.Wecouldaddalltheseroutesmanuallytoroutes/web.php,butthere’salreadyaconveniencetoolforthat,calledAuth::routes():

//routes/web.php

Auth::routes();

Asyoucanprobablyguess,Auth::routes()bringsinabundleofpredefinedroutestoyourroutesfile.InExample9-5youcanseetheroutesthatareactuallybeingdefinedthere.

Example9-5.TheroutesprovidedbyAuth::routes()//AuthenticationRoutes

$this->get('login','Auth\LoginController@showLoginForm');

$this->post('login','Auth\LoginController@login');

$this->get('logout','Auth\LoginController@logout');

//RegistrationRoutes

$this->get('register','Auth\RegisterController@showRegistrationForm');

$this->post('register','Auth\RegisterController@register');

//PasswordResetRoutes

$this->get('password/reset','Auth\ForgotPasswordController@showLinkRequestForm');

$this->post('password/email','Auth\ForgotPasswordController@sendResetLinkEmail');

$this->get('password/reset/{token}','Auth\ResetPasswordController@showResetForm');

$this->post('password/reset','Auth\ResetPasswordController@reset');

Basically,Auth::routes()includestheroutesforauthentication,registration,andpasswordresets.

LARAVEL’SCONTROLLER/METHODREFERENCESYNTAX

Laravelhasaconventionforhowtorefertoaparticularmethodinagivencontroller:ControllerName@methodName.Sometimesthisisjustacasualcommunicationconvention,butit’salsousedinrealbindings,likeinExample9-5.Laravelparseswhat’sbeforeandafterthe@andusesthosesegmentstoidentifythecontrollerandmethod.

Page 357: Laravel: Up and Running: A Framework for Building Modern PHP

TheAuthScaffoldAtthispointyouhaveamigration,amodel,controllers,androutesforyourauthenticationsystem.Butwhataboutyourviews?

Laravelhandlesthatbyprovidinganauthscaffold(newinLaravel5.2),whichisintendedtoberunonanewapplicationandprovideyouwithevenmoreskeletoncodetogetyourauthsystemrunningquickly.

TheauthscaffoldtakescareofaddingAuth::routes()toyourroutesfile,addsaviewforeachroute,andcreatesaHomeControllertoserveasthelandingpageforlogged-inusers;italsoroutestotheindex()methodofHomeControlleratthe/homeURI.

Justrunphpartisanmake:auth,andthefollowingfileswillbemadeavailabletoyou:

app/Http/Controllers/HomeController.php

resources/views/auth/login.blade.php

resources/views/auth/register.blade.php

resources/views/auth/passwords/email.blade.php

resources/views/auth/passwords/reset.blade.php

resources/views/layouts/app.blade.php

resources/views/home.blade.php

Atthispoint,youhave/returningthewelcomeview,/homereturningthehomeview,andaseriesofauthroutesforlogin,logout,registration,andpasswordresetpointingtotheauthcontrollers.EachoftheseededviewshasBootstrap-basedlayoutsandformfieldsforallnecessaryfieldsforlogin,registration,andpasswordreset,andtheyalreadypointtothecorrectroutes.

Atthispoint,youhaveallofthepiecesinplaceforeverystepofthenormaluserregistrationandauthenticationflow.Youcantweakallyouwant,butyou’reentirelyreadytoregisterandauthenticateusers.

Let’sreviewquicklythestepsfromnewsitetofullauthenticationsystem:

laravelnewMyApp

cdMyApp

phpartisanmake:auth

phpartisanmigrate

That’sit.Runthosecommands,andyouwillhavealandingpageandabootstrap-baseduserregistration,login,logout,andpasswordresetsystem,withabasiclandingpageforallauthenticatedusers.

Page 358: Laravel: Up and Running: A Framework for Building Modern PHP

“RememberMe”Theauthscaffoldhasthisimplementedoutofthebox,butit’sstillworthlearninghowitworksandhowtouseitonyourown.Ifyouwanttoimplementa“rememberme”–stylelong-livedaccesstoken,makesureyouhavearemember_tokencolumnonyouruserstable(whichyouwillifyouusedthedefaultmigration).

Whenyou’renormallylogginginauser(andthisishowtheLoginControllerdoesit,withtheAuthenticatesUserstrait),you’ll“attempt”anauthenticationwiththeuser-providedinformation,likeinExample9-6.

Example9-6.Attemptingauserauthenticationif(auth()->attempt([

'email'=>request()->input('email'),

'password'=>request()->input('password')

])){

//Handlethesuccessfullogin

}

Thisprovidesyouwithauserloginthatlastsaslongastheuser ’ssession.IfyouwantLaraveltoextendtheloginindefinitelyusingcookies(aslongastheuserisonthesamecomputeranddoesn’tlogout),youcanpassabooleantrueasthesecondparameteroftheauth()->attempt()method.TakealookatExample9-7toseewhatthatrequestlookslike.

Example9-7.Attemptingauserauthenticationwitha“rememberme”checkboxcheckif(auth()->attempt([

'email'=>request()->input('email'),

'password'=>request()->input('password')

]),request()->has('remember')){

//Handlethesuccessfullogin

}

Youcanseethatwecheckedwhethertheinputhasarememberproperty,whichwillreturnaboolean.Thisallowsouruserstodecideiftheywanttoberememberedwithacheckboxintheloginform.

Andlater,ifyouneedtomanuallycheckwhetherthecurrentuserwasauthenticatedbyaremembertoken,there’samethodforthat:auth()->viaRemember()returnsabooleanindicatingwhetherornotthecurrentuserauthenticatedviaaremembertoken.Thiswillallowyoutopreventcertainhigher-sensitivityfeaturesfrombeingaccessiblebyremembertoken,andyoucanrequireuserstoreentertheirpasswords.

Page 359: Laravel: Up and Running: A Framework for Building Modern PHP

ManuallyAuthenticatingUsersThemostcommoncaseforuserauthenticationisthatyou’llallowtheuserstoprovidetheircredentials,andthenuseauth()->attempt()toseewhethertheprovidedcredentialsmatchanyrealusers.Ifso,youlogthemin.

Butsometimestherearecontextswhereit’svaluableforyoutobeabletochoosetologauserinonyourown.Forexample,youmaywanttoallowadminuserstoswitchusers.

Therearetwomethodsthatmakethispossible.First,youcanjustpassauserID:

auth()->loginUsingId(5);

Second,youcanpassaUserobject(oranyotherobjectthatimplementstheIlluminate\Contracts\Auth\Authenticatablecontract):

auth()->login($user);

Page 360: Laravel: Up and Running: A Framework for Building Modern PHP

AuthMiddlewareInExample9-3,wesawhowtocheckwhethervisitorsareloggedinandredirectthemifnot.Youcouldperformthesesortsofchecksoneveryrouteinyourapplication,butitwouldveryquicklygettedious.Itturnsoutthatroutemiddleware(seeChapter10tolearnmoreabouthowtheywork)areaperfectfitforrestrictingcertainroutestoguestsortoauthenticatedusers.

Onceagain,Laravelcomeswiththemiddlewareweneedtodothisoutofthebox.YoucanseewhichroutemiddlewareyouhavedefinedinApp\Http\Kernel:

protected$routeMiddleware=[

'auth'=>\Illuminate\Auth\Middleware\Authenticate::class,

'auth.basic'=>\Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,

'bindings'=>\Illuminate\Routing\Middleware\SubstituteBindings::class,

'can'=>\Illuminate\Auth\Middleware\Authorize::class,

'guest'=>\App\Http\Middleware\RedirectIfAuthenticated::class,

'throttle'=>\Illuminate\Routing\Middleware\ThrottleRequests::class,

];

Threeofthedefaultroutemiddlewareareauthentication-related:authrestrictsrouteaccesstoauthenticatedusers,auth.basicrestrictsaccesstoauthenticatedusersusingHTTPBasicAuthentication,andguestrestrictsaccesstounauthenticatedusers.canisusedforauthorizinguseraccesstogivenroutes.

It’smostcommontouseauthforyourauthenticated-user-onlysectionsandguestforanyroutesyoudon’twantauthenticateduserstosee(liketheloginform).auth.basicisamuchlesscommonlyusedmiddlewareforauthenticatingviarequestheaders.

Example9-8showsafewsampleroutesprotectedbytheauthmiddleware.

Example9-8.SampleroutesprotectedbyauthmiddlewareRoute::group(['middleware'=>'auth'],function(){

Route::get('account','AccountController@dashboard');

});

Route::get('login','Auth\LoginController@getLogin')->middleware('guest');

Page 361: Laravel: Up and Running: A Framework for Building Modern PHP

GuardsEveryaspectofLaravel’sauthenticationsystemisroutedthroughsomethingcalledaguard.Eachguardisacombinationoftwopieces:adriverthatdefineshowitpersistsandretrievestheauthenticationstate(forexample,session),andaproviderthatallowsyoutogetauserbycertaincriteria(forexample,users).

OutoftheboxLaravelhastwoguards:webandapi.webisthemoretraditionalauthenticationstyle,usingthesessiondriverandthebasicuserprovider.apialsousesthesameuserprovider,butitusesthetokendriverinsteadofthesessiontoauthenticateeachrequest.

You’dchangedriversifyouwantedtohandletheidentificationandpersistenceofauser ’sidentitydifferently(forexample,changingfromalong-runningsessiontoaprovided-every-page-loadtoken),andyou’dchangeprovidersifyouwantedtochangethestoragetypeorretrievalmethodsforyourusers(forexample,movingtostoringyourusersinMongoinsteadofMySQL).

Page 362: Laravel: Up and Running: A Framework for Building Modern PHP

ChangingtheDefaultGuardTheguardsaredefinedinconfig/auth.php,andyoucanchangethem,addnewguards,andalsodefinewhichguardwillbethedefaultthere.

Thedefaultguardwillbethatwhichisusedanytimeyouuseanyauthfeatures.auth()->user()willpullthecurrentlyauthenticateduserusingthedefaultguard.Youcanchangethisguardbychangingtheauth.defaults.guardsettinginconfig/auth.php:

'defaults'=>[

'guard'=>'web',//Changethedefaulthere

'passwords'=>'users',

],

Ifyou’reusingLaravel5.1,you’llnoticethatthestructureoftheauthenticationinformationisalittledifferentfromthis.Don’tworry;thefeaturesallstillworkthesame,they’rejuststructureddifferently.

Page 363: Laravel: Up and Running: A Framework for Building Modern PHP
Page 364: Laravel: Up and Running: A Framework for Building Modern PHP

CONFIGURATIONCONVENTIONSYoumayhavenoticedthatIrefertoconfigurationsectionswithreferenceslikeauth.defaults.guard.Whatthattranslatestois:inconfig/auth.php,inthearraysectionkeyeddefaults,thereshouldbeapropertykeyedguard.Thatoneisauth.defaults.guard.

Page 365: Laravel: Up and Running: A Framework for Building Modern PHP

UsingOtherGuardsWithoutChangingtheDefaultIfyouwanttouseanotherguard,butnotchangethedefault,youcanstartyourAuthcallswithguard():

$apiUser=auth()->guard('api')->user();

Thiswill,justforthiscall,getthecurrentuserusingtheapiguard.

Page 366: Laravel: Up and Running: A Framework for Building Modern PHP

AddingaNewGuardYoucanaddanewguardatanytimeinconfig/auth.php,intheauth.guardssetting:

'guards'=>[

'trainees'=>[

'driver'=>'session',

'provider'=>'trainees',

],

],

Asyoucanseehere,we’vecreatedanewguard(inadditiontowebandapi)namedtrainees.Let’simagine,fortherestofthissection,thatwe’rebuildinganappwhereourusersarephysicaltrainersandtheyeachhavetheirownusers—trainees—whocanlogintotheirsubdomains.So,weneedaseparateguardforthem.

Theonlytwooptionsfordriveraretokenandsession.Outofthebox,theonlyoptionforproviderisusers,butyoucancreateyourownprovidereasily.

Page 367: Laravel: Up and Running: A Framework for Building Modern PHP

CreatingaCustomUserProviderJustbelowwheretheguardsaredefinedinconfig/auth.php,there’sanauth.providerssectionthatdefinestheavailableproviders.Let’screateanewprovidernamedtrainees:

'providers'=>[

'users'=>[

'driver'=>'eloquent',

'model'=>App\User::class,

],

'trainees'=>[

'driver'=>'eloquent',

'model'=>App\Trainee::class,

],

],

Thetwooptionsfordriverareeloquentanddatabase;ifyouuseeloquent,you’llneedamodelpropertythatcontainsanEloquentclassname(themodeltouseforyourUserclass),andifyouusedatabase,you’llneedatablepropertytodefinewhichtableitshouldauthenticateagainst.

Inourexample,youcanseethatthisapplicationhasaUserandaTrainee,andtheyneedtobeauthenticatedseparately.Thisway,thecodecandifferentiatebetweenauth()->guard(users)andauth()->guard(trainees).

Onelastnote:theauthroutemiddlewarecantakeaparameterthatistheguardname.So,youcanguardcertainrouteswithaspecificguard:

Route::group(['middleware'=>'auth:trainees'],function(){

//Trainee-onlyrouteshere

});

Page 368: Laravel: Up and Running: A Framework for Building Modern PHP

CustomUserProvidersforNonrelationalDatabasesTheuserprovidercreationflowjustdescribedstillreliesonthesameUserProviderclass,whichmeansit’sexpectingtopulltheidentifyinginformationoutofarelationaldatabase.Butifyou’reusingMongoorRiakorsomethingsimilar,you’llactuallyneedtocreateyourownclass.

Todothis,createanewclassthatimplementstheIlluminate\Contracts\Auth\UserProviderinterface,andthenbinditinAuthServiceProvider@boot:

auth()->provider('riak',function($app,array$config){

//ReturnaninstanceofIlluminate\Contracts\Auth\UserProvider...

returnnewRiakUserProvider($app['riak.connection']);

});

Page 369: Laravel: Up and Running: A Framework for Building Modern PHP

AuthEventsWe’lltalkmoreabouteventsinChapter16,butLaravel’seventsystemisabasicpub/subframework.Therearesystem-anduser-generatedeventsthatarebroadcast,andtheuserhastheabilitytocreateeventlistenersthatdocertainthingsinresponsetocertainevents.

So,whatifyouwantedtosendapingtoaparticularsecurityserviceeverytimeauserwaslockedoutaftertoomanyfailedloginattempts?Maybethisservicewatchesoutforacertainnumberoffailedloginsfromcertaingeographicregions,orsomethingelse.Youcould,ofcourse,injectacallintheappropriatecontroller.Butwithevents,youcanjustcreateaneventlistenerthatlistenstothe“userlockedout”event,andregisterthat.

TakealookatExample9-9toseealloftheeventsthattheauthenticationsystememits.

Example9-9.Authenticationeventsgeneratedbytheframeworkprotected$listen=[

'Illuminate\Auth\Events\Attempting'=>[],

'Illuminate\Auth\Events\Login'=>[],

'Illuminate\Auth\Events\Logout'=>[],

'Illuminate\Auth\Events\Lockout'=>[],

];

Asyoucansee,therearelistenersfor“userattemptinglogin,”“successfullogin,”“logout,”and“lockout.”Tolearnmoreabouthowtobuildeventlistenersfortheseevents,checkoutChapter16.

Page 370: Laravel: Up and Running: A Framework for Building Modern PHP

Authorization(ACL)andRolesFinally,let’scoverLaravel’sauthorizationsystem.Itenablesyoutodeterminewhetherauserisauthorizedtodoaparticularthing,whichyou’llcheckusingafewprimaryverbs:can,cannot,allows,anddenies.Theaccesscontrollist(ACL)systemisnewinLaravel5.2.

MostofthisauthorizationcontrolwillbeperformedusingtheGatefacade,buttherearealsoconveniencehelpersinyourcontrollers,ontheUsermodel,asmiddleware,andavailableasBladedirectives.Takealookatthisexampletogetatasteofwhatwe’llbeabletodo:

if(Gate::denies('edit',$contact)){

abort(403);

}

if(!Gate::check('create',Contact::class)){

abort(403);

}

Page 371: Laravel: Up and Running: A Framework for Building Modern PHP

DefiningAuthorizationRulesThedefaultplacetodefineauthorizationrulesistheboot()methodoftheAuthServiceProvider.ItshouldalreadyhaveaninstanceofIlluminate\Contracts\Auth\Access\Gate(aliasedasGateContract)typehintedandinjectedas$gate.

Anauthorizationruleiscalledanability,andiscomprisedoftwothings:astringkey(e.g.,update-contact)andaclosurethatreturnsaboolean.Example9-10showsanabilityforupdatingacontact.

Example9-10.SampleabilityforupdatingacontactclassAuthServiceProviderextendsServiceProvider

{

publicfunctionboot(GateContract$gate)

{

$this->registerPolicies($gate);

$gate->define('update-contact',function($user,$contact){

return$user->id===$contact->user_id;

});

}

}

Let’swalkthroughthestepsfordefininganability.

First,youwanttodefineakey.Innamingthiskey,youshouldconsiderwhatstringmakessenseinyourcode’sflowtorefertotheabilityyou’reprovidingtheuser.Youcanseethecodesampleusestheconvention{verb}-{modelName}:create-contact,update-contact,etc.

Second,youdefinetheclosure.Thefirstparameterwillbethecurrentlyauthenticateduser,andallparametersafterthatwillbetheobject(s)you’recheckingforaccessto—inthisinstance,thecontact.

So,giventhosetwoobjects,wecancheckwhethertheuserisauthorizedtoupdatethiscontact.Youcanwritethislogichoweveryouwant,butintheappwe’relookingatatthemoment,authorizationdependsonbeingthecreatorofthecontactrow.Theclosurewillreturntrue(authorized)ifthecurrentusercreatedthecontact,andfalse(unauthorized)ifnot.

Justlikewithroutedefinitions,youcouldalsouseaclassandmethodinsteadofaclosuretoresolvethisdefinition:

$gate->define('update-contact','ContactACLChecker@updateContact');

Page 372: Laravel: Up and Running: A Framework for Building Modern PHP

TheGateFacade(andInjectingGate)Nowthatyou’vedefinedanability,it’stimetotestagainstit.ThesimplestwayistousetheGatefacade,asinExample9-11(oryoucaninjectaninstanceofIlluminate\Contracts\Auth\Access\Gate).

Example9-11.BasicGatefacadeusageif(Gate::allows('update-contact',$contact)){

//Updatecontact

}

//or...

if(Gate::denies('update-contact',$contact)){

abort(403);

}

Youmightalsodefineanabilitywithmultipleparameters—maybeacontactcanbeingroups,andyouwanttoauthorizewhethertheuserhasaccesstoaddacontacttoagroup.Example9-12showshowtodothis.

Example9-12.Abilitieswithmultipleparameters//Definition

$gate->define('add-contact-to-group',function($user,$contact,$group){

return$user->id===$contact->user_id&&$user->id===$group->user_id;

});

//Usage

if(Gate::denies('add-contact-to-group',[$contact,$group])){

abort(403);

}

Andifyouneedtocheckauthorizationforauserotherthanthecurrentlyauthenticateduser,tryforUser(),likeinExample9-13.

Example9-13.SpecifyingtheuserforGateif(Gate::forUser($user)->denies('create-contact')){

abort(403);

}

Page 373: Laravel: Up and Running: A Framework for Building Modern PHP

TheAuthorizeMiddlewareIfyouwanttoauthorizeentireroutes,youcanusetheAuthorizemiddleware(whichhasashortcutofcan),likeinExample9-14.

Example9-14.UsingtheAuthorizemiddlewareRoute::get('people/create',function(){

//Createperson...

})->middleware('can:create-person');

Route::get('people/{person}/edit',function(){

//Createperson...

})->middleware('can:create,person');

Here,the{person}parameter(whetherit’sdefinedasastringorasaboundroutemodel)willbepassedtotheabilitymethodasanadditionalparameter.

Page 374: Laravel: Up and Running: A Framework for Building Modern PHP

ControllerAuthorizationTheparentApp\Http\Controllers\ControllerclassinLaravelimportstheAuthorizesRequeststrait,whichprovidesthreemethodsforauthorization:authorize(),authorizeForUser(),andauthorizeResource().

authorize()takesanabilitykeyandanobject(orarrayofobjects)asparameters,andiftheauthorizationfails,it’llquittheapplicationwitha403(Unauthorized)statuscode.Thatmeansthisfeaturecanturnthreelinesofauthorizationcodeintojustone,asyoucanseeinExample9-15.

Example9-15.Simplifyingcontrollerauthorizationwithauthorize()//Fromthis:

publicfunctionshow($contactId)

{

$contact=Contact::findOrFail($contactId);

if(Gate::cannot('update-contact',$contact)){

abort(403);

}

}

//Tothis:

publicfunctionshow($contactId)

{

$contact=Contact::findOrFail($contactId);

$this->authorize('update-contact',$contact);

}

authorizeForUser()isthesame,butallowsyoutopassinaUserobjectinsteadofdefaultingtothecurrentlyauthenticateduser:

$this->authorizeForUser($user,'update-contact',$contact);

authorizeResource(),calledonceinthecontrollerconstructor,mapsapredefinedsetofauthorizationrulestoeachoftheRESTfulcontrollermethodsinthatcontroller—somethinglikeExample9-16.

Example9-16.Theauthorization-to-methodmappingsofauthorizeResource()...

classContactsControllerextendsController

{

publicfunction__construct()

{

//Thiscalldoeseverythingyouseeinthemethodsbelow.

//Ifyouputthishere,youcanremoveallauthorize

//callsintheindividualresourcemethodshere.

$this->authorizeResource(Contact::class);

}

publicfunctionindex()

{

$this->authorize('view',Contact::class);

}

publicfunctioncreate()

{

$this->authorize('create',Contact::class);

Page 375: Laravel: Up and Running: A Framework for Building Modern PHP

}

publicfunctionstore(Request$request)

{

$this->authorize('create',Contact::class);

}

publicfunctionshow(Contact$contact)

{

$this->authorize('view',$contact);

}

publicfunctionedit(Contact$contact)

{

$this->authorize('update',$contact);

}

publicfunctionupdate(Request$request,Contact$contact)

{

$this->authorize('update',$contact);

}

publicfunctiondestroy(Contact$contact)

{

$this->authorize('delete',$contact);

}

}

Page 376: Laravel: Up and Running: A Framework for Building Modern PHP

CheckingontheUserInstanceIfyou’renotinacontroller,you’remorelikelytobecheckingthecapabilitiesofaspecificuserthanthecurrentlyauthenticateduser.That’salreadypossiblewiththeGatefacadeusingtheforUser()method,butsometimesthesyntaxcanfeelalittleoff.

Thankfully,theAuthorizabletraitontheUserclassprovidesthreemethodstomakeamorereadableauthorizationfeature:$user->can(),$user->cant(),and$user->cannot().Asyoucanprobablyguess,cant()andcannot()dothesamething,andcan()istheirexactinverse.

ThatmeansyoucandosomethinglikeExample9-17.

Example9-17.Checkingauthorizationonauserinstance$user=User::find(1);

if($user->can('create-contact')){

//dosomething

}

Behindthescenes,thesemethodsarejustpassingyourparamstoGate;intheprecedingexample,Gate::forUser($user)->can('create-contact').

Page 377: Laravel: Up and Running: A Framework for Building Modern PHP

BladeChecksBladealsohasalittleconveniencehelper:[email protected].

Example9-18.UsingBlade’s@candirective<nav>

<ahref="/">Home</a>

@can('edit-contact',$contact)

<ahref="{{route('contacts.edit',[$contact->id])}}">EditThisContact</a>

@endcan

</nav>

Youcanalsouse@elseinbetween@canand@endcan,andyoucanuse@cannotand@endcannotasinExample9-19.

Example9-19.UsingBlade’s@cannotdirective<h1>{{$contact->name}}</h1>

@cannot('edit-contact',$contact)

LOCKED

@endcannot

Page 378: Laravel: Up and Running: A Framework for Building Modern PHP

InterceptingChecksIfyou’veeverbuiltanappwithanadminuserclass,you’veprobablylookedatallofthesimpleauthorizationclosuressofarinthischapterandthoughtabouthowyoucouldaddasuperuserclassthatoverridesthesechecksineverycase.Thankfully,there’salreadyatoolforthat.

InAuthServiceProvider,whereyou’realreadydefiningyourabilities,youcanalsoaddabefore()checkthatrunsbeforealltheothersandcanoptionallyoverridethem,likeinExample9-20.

Example9-20.OverridingGatecheckswithbefore()$gate->before(function($user,$ability){

if($user->isOwner()){

returntrue;

}

});

Notethatthestringnamefortheabilityisalsopassedin,soyoucandifferentiateyourbeforehooksbasedonyourabilitynamingscheme.

Page 379: Laravel: Up and Running: A Framework for Building Modern PHP

PoliciesUpuntilthispoint,alloftheaccesscontrolshaverequiredyoutomanuallyassociateEloquentmodelswiththeabilitynames.Youcouldhavecreatedanabilitynamedsomethinglikevisit-dashboardthat’snotrelatedtoaspecificEloquentmodel,butyou’llprobablyhavenoticedthatmostofourexampleshavehadtodowithdoingsomethingtosomething—andinmostofthesecases,thesomethingthat’stherecipientoftheactionisanEloquentmodel.

Authorizationpoliciesareorganizationalstructuresthathelpyougroupyourauthorizationlogicbasedontheresourceyou’recontrollingaccessto.TheymakeiteasytomanagedefiningauthorizationrulesforbehaviortowardaparticularEloquentmodel(orotherPHPclass),alltogetherinasinglelocation.

GeneratingpoliciesPoliciesarePHPclasses,whichcanbegeneratedwithanArtisancommand:

phpartisanmake:policyContactPolicy

Oncethey’regenerated,theyneedtoberegistered.TheAuthServiceProviderhasa$policiesproperty,whichisanarray.Thekeyofeachitemistheclassnameoftheprotectedresource(usually,butnotalways,anEloquentclass),andthevalueisthepolicyclassname:

classAuthServiceProviderextendsServiceProvider

{

protected$policies=[

Contact::class=>ContactPolicy::class,

]

Apolicyclassthat’sgeneratedbyArtisandoesn’thaveanyspecialpropertiesormethods.Buteverymethodthatyouaddisnowmappedasanabilitykeyforthisobject.

Let’sdefineanupdate()methodtotakealookathowitworks(Example9-21).

Example9-21.Asampleupdate()policymethod<?php

namespaceApp\Policies;

classContactPolicy

{

publicfunctionupdate($user,$contact)

{

return$user->id===$contact->user_id;

}

}

NoticethatthecontentsofthismethodlookexactlyliketheywouldinaGatedefinition.

Page 380: Laravel: Up and Running: A Framework for Building Modern PHP

POLICYMETHODSTHATDON’TTAKEANINSTANCE,BEFORE5.3InLaravel5.2,ifyouwanttodefineapolicymethodthatrelatestotheclassbutnotaspecificinstance—forexample,“canthisusercreatecontactsatall?”ratherthanjust“canthisuserviewthisspecificcontact?”—createthatmethodandadd“Any”attheendofitsname:

...

classContactPolicy

{

publicfunctioncreateAny($user)

{

return$user->canCreateContacts();

}

InLaravel5.3,youcandroptheAnysuffixandtreatthisjustlikeanormalmethod.

CheckingpoliciesIfthere’sapolicydefinedforaresourcetype,theGatewillusethefirstparametertofigureoutwhichmethodtocheckonyourpolicy.IfyourunGate::allows('update',$contact),itwillchecktheContactPolicy@updatemethodforauthorization.

ThisalsoworksfortheAuthorizemiddlewareandforUsermodelcheckingandBladechecking,asseeninExample9-22.

Example9-22.Checkingauthorizationagainstapolicy//Gate

if(Gate::denies('update',$contact)){

abort(403);

}

//Gateifyoudon'thaveanexplicitinstance

if(!Gate::check('create',Contact::class)){

abort(403);

}

//User

if($user->can('update',$contact)){

//Dostuff

}

//Blade

@can('update',$contact)

<!--showstuff-->

@endcan

Additionally,there’sapolicy()helperthatallowsyoutoretrieveapolicyclassandrunitsmethods:

if(policy($contact)->update($user,$contact)){

//Dostuff

}

OverridingpoliciesJustlikewithnormalabilitydefinitions,policiescandefineabefore()methodthatallows

Page 381: Laravel: Up and Running: A Framework for Building Modern PHP

youtooverrideanycallbeforeit’sevenprocessed(seeExample9-23).

Example9-23.Overridingpolicieswiththebefore()methodpublicfunctionbefore($user,$ability)

{

if($user->isAdmin()){

returntrue;

}

}

PASSPORTANDOAUTH

There’saLaravelpackagecalledPassportthatmakesiteasytosetupyourownOAuthserverasapartofyourLaravelapp.Takealookat“APIAuthenticationwithLaravelPassport”tolearnmoreabouthowitworks.

Page 382: Laravel: Up and Running: A Framework for Building Modern PHP

TestingApplicationtestsoftenneedtoperformaparticularbehavioronbehalfofaparticularuser.Wethereforeneedtobeabletoauthenticateasauserinapplicationtests,andweneedtotestauthorizationrulesandauthenticationroutes.

Ofcourse,it’spossibletowriteanapplicationtestthatmanuallyvisitstheloginpageandthenfillsouttheformandsubmitsit,butthat’snotnecessary.Instead,thesimplestoptionistousethe->be()methodtosimulatebeingloggedinasauser.TakealookatExample9-24.

Example9-24.Authenticatingasauserinapplicationtestspublicfunctiontest_it_creates_a_new_contact()

{

$user=factory(User::class)->create();

$this->be($user);

$this->post('contacts',[

'email'=>'[email protected]'

]);

$this->seeInDatabase('contacts',[

'email'=>'[email protected]',

'user_id'=>$user->id,

]);

}

WecanalsotestauthorizationlikeinExample9-25.

Example9-25.Testingauthorizationrulespublicfunctiontest_non_admins_cant_create_users()

{

$user=factory(User::class)->create([

'admin'=>false

]);

$this->be($user);

$this->post('users',['email'=>'[email protected]']);

$this->dontSeeInDatabase('users',[

'email'=>'[email protected]'

]);

}

Orwecantestfora403responselikeinExample9-26.

Example9-26.Testingauthorizationrulesbycheckingstatuscodepublicfunctiontest_non_admins_cant_create_users()

{

$user=factory(User::class)->create([

'admin'=>false

]);

$this->be($user);

$this->post('users',['email'=>'[email protected]']);

$this->assertResponseStatus(403);

}

Weneedtotestthatourauthentication(signupandsignin)routesworktoo,asillustratedinExample9-27.

Page 383: Laravel: Up and Running: A Framework for Building Modern PHP

Example9-27.Testingauthenticationroutespublicfunctiontest_users_can_register()

{

$this->post('register',[

'name'=>'SalLeibowitz',

'email'=>'[email protected]',

'password'=>'abcdefg123',

'password_confirmation'=>'abcdefg123',

]);

$this->followRedirects()->assertResponseOk();

$this->seeInDatabase('users',[

'name'=>'SalLeibowitz',

'email'=>'[email protected]',

]);

}

publicfunctiontest_users_can_log_in()

{

$user=factory(User::class)->create([

'password'=>bcrypt('abcdefg123')

]);

$this->post('login',[

'email'=>$user->email,

'password'=>'abcdefg123',

]);

$this->followRedirects()->assertResponseOk();

$this->assertTrue(auth()->check());

}

Youcouldalsousetheintegrationtestfeaturestodirectthetestto“click”yourauthenticationfieldsand“submit”thefieldstotesttheentireflow.

Page 384: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRBetweenthedefaultUsermodel,thecreate_users_tablemigration,theauthcontrollers,andtheauthscaffold,Laravelcomeswithafulluserauthenticationsystemoutofthebox.TheRegisterControllerhandlesuserregistration,theLoginControllerhandlesuserauthentication,andtheResetPasswordControllerandtheForgotPasswordControllerhandlepasswordresets.Eachhascertainpropertiesandmethodsthatcanbeusedtooverridesomeofthedefaultbehavior.

TheAuthfacadeandtheauth()globalhelperprovideaccesstothecurrentuser(auth()->user())andmakesiteasytocheckwhetherauserisloggedin(auth()->check()andauth()->guest()).

Laravelalsohasanauthorizationsystembuiltinthatallowsyoutodefinespecificabilities(create-contact,visit-secret-page)ordefinepoliciesforuserinteractionwithentiremodels.

YoucancheckforauthorizationwiththeGatefacade,the->can()and->cannot()methodsontheUserclass,the@canand@cannotdirectivesinBlade,the->authorize()methodsonthecontroller,orthecanmiddleware.

Page 385: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter10.RequestsandResponses

We’vealreadycoveredtheIlluminateRequestobjectabit—howyoucantypehintitinconstructorstogetaninstance,andthenusethattogetinformationabouttheuser ’sinput.InChapter3,forexample,wesawhowyoucantypehintitinconstructorstogetaninstance,andinChapter6welookedathowyoucanuseittogetinformationabouttheuser ’sinput.

Inthischapter,we’lllearnmoreaboutwhatthatRequestobjectis,howit’sgeneratedandwhatitrepresents,andwhatpartitplaysinyourapplication’slifecycle.We’llalsotalkabouttheResponseobjectandLaravel’simplementationofthemiddlewarepattern.

Page 386: Laravel: Up and Running: A Framework for Building Modern PHP

Laravel’sRequestLifecycleEveryrequestcomingintoaLaravelapplication,whethergeneratedbyanHTTPrequestoracommand-lineinteraction,isimmediatelyconvertedintoanIlluminateRequestobject,whichthencrossesmanylayersandendsupbeingparsedbytheapplicationitself.TheapplicationthengeneratesanIlluminateResponseobject,whichissentbackoutacrossthoselayersandfinallyreturnedtotheenduser.

Thisrequest/responselifecycleisillustratedinFigure10-1.

Let’stakealookatwhatittakestomakeeachofthesestepshappen,fromthefirstlineofcodetothelast.

EveryLaravelapplicationhassomeformofconfigurationsetupatthewebserverlevel,inan.htaccessfileoranNginxconfigurationsettingorsomethingsimilar,thatcaptureseverywebrequestregardlessofURLandroutesittopublic/index.phpintheLaravelapplicationdirectory(app).

Figure10-1.Request/responselifecycle

Page 387: Laravel: Up and Running: A Framework for Building Modern PHP

BootstrappingtheApplicationindex.phpdoesn’tactuallyhavethatmuchcodeinit.Ithasthreeprimaryfunctions.

First,itloadsComposer ’sautoloadfileandLaravel’scompiledapplicationcache,whichlivesatbootstrap/cache/compiled.php.Thisfileiswhat’sgeneratedwhenyourunphpartisanoptimize,anditpreloadsallofthemostcommonlyusedclassesforfasterloading.

COMPOSER ANDLARAVEL

Laravel’scorefunctionalityisseparatedintoaseriesofcomponentsundertheIlluminatenamespace,whichareallpulledintoeachLaravelappusingComposer.LaravelalsopullsinquiteafewpackagesfromSymfonyandseveralothercommunity-developedpackages.Inthisway,Laravelisjustasmuchanopinionatedcollectionofcomponentsasitisaframework.

Next,itkicksoffLaravel’sbootstrap,creatingtheapplicationcontainer(you’lllearnmoreaboutthecontainerinChapter11)andregisteringafewcoreservices(includingthekernel,whichwe’lltalkaboutinjustabit).

Finally,itcreatesaninstanceofthekernel,createsarequestrepresentingthecurrentuser ’swebrequest,andpassestherequesttothekerneltohandle.ThekernelrespondswithanIlluminateResponseobject,whichindex.phpthenreturnstotheenduser,andterminatesthepagerequest.

Laravel’skernelThekernelisthecorerouterofeveryLaravelapplication,responsiblefortakinginauserrequest,processingitthroughmiddlewareandhandlingexceptionsandpassingittothepagerouter,andthenreturningthefinalresponse.Thereareactuallytwokernels,butonlyoneisusedforeachpagerequest.Oneoftheroutershandleswebrequests(theHTTPkernel)andtheotherhandlesconsole,cron,andArtisanrequests(theconsolekernel).Eachhasahandle()methodthat’sresponsiblefortakinginanIlluminateRequestobjectandreturninganIlluminateResponseobject.

Thekernelrunsallofthebootstrapsthatneedtorunbeforeeveryrequest,includingdeterminingwhichenvironmentthecurrentrequestisrunningin(staging,local,production,etc.)andrunningalloftheserviceproviders.TheHTTPkerneladditionallydefinesthelistofmiddlewarethatwillwrapeachrequest,includingthecoremiddlewareresponsibleforsessionsandCSRFprotection.

Page 388: Laravel: Up and Running: A Framework for Building Modern PHP

ServiceProvidersWhilethere’sabitofproceduralcodeinthesebootstraps,almostallofLaravel’sbootstrapcodeisseparatedintosomethingLaravelcallsserviceproviders.Aserviceproviderisaclassthatencapsulateslogicthatvariouspartsofyourapplicationneedtoruninordertobootstraptheircorefunctionality.

Forexample,there’sanAuthServiceProviderthatbootstrapsalloftheregistrationsnecessaryforLaravel’sauthenticationsystemandaRouteServiceProviderthatbootstrapstheroutingsystem.

Theconceptofserviceproviderscanbealittlehardtounderstandatfirst,sothinkaboutitthisway:manycomponentsofyourapplicationhavebootstrapcodethatneedstorunwhentheapplicationinitializes.Serviceprovidersareatoolforgroupingthatbootstrapcodeintorelatedclasses.Ifyouhaveanycodethatneedstoruninpreparationforyourapplicationcodetowork,it’sastrongcandidateforaserviceprovider.

Forexample,ifyoueverfindthatthefeatureyou’reworkingonrequiressomeclassesregisteredinthecontainer(we’lllearnmoreaboutthisinChapter11),youwouldcreateaserviceproviderjustforthatpieceoffunctionality.YoumighthaveaGitHubServiceProvideroraMailerServiceProvider.

Serviceprovidershavetwoimportantmethods:boot()andregister().There’salsoa$deferpropertythatyoumightchoosetouse.Here’showtheywork.

First,alloftheserviceproviders’register()methodsarecalled.Thisiswherewewanttobindclassesandaliasestothecontainer.Youdon’twanttodoanythinginregister()thatreliesontheentireapplicationbeingbootstrapped.

Second,alloftheserviceproviders’boot()methodsarecalled.Youcannowdoanyotherbootstrappinghere,likebindingeventlistenersordefiningroutes—anythingthatmayrelyontheentireLaravelapplicationhavingbeenbootstrapped.

Ifyourserviceproviderisonlygoingtoregisterbindingsinthecontainer(i.e.,teachthecontainerhowtoresolveagivenclassorinterface),butnotperformanyotherbootstrapping,youcan“defer”itsregistrations,whichmeanstheywon’trununlessoneoftheirbindingsisexplicitlyrequestedfromthecontainer.Thiscanspeedupyourapplication’saveragetimetobootstrap.

Ifyouwanttodeferyourserviceprovider ’sregistrations,firstgiveitaprotected$deferpropertyandsetittotrue,andthengiveitaprovides()methodthatreturnsalistofbindingstheproviderprovides,asshowninExample10-1.

Example10-1.Deferringtheregistrationofaserviceprovider...

classGitHubServiceProviderextendsServiceProvider

{

protected$defer=true;

Page 389: Laravel: Up and Running: A Framework for Building Modern PHP

publicfunctionprovides()

{

return[

GitHubClient::class

];

}

Page 390: Laravel: Up and Running: A Framework for Building Modern PHP
Page 391: Laravel: Up and Running: A Framework for Building Modern PHP

MOREUSESFORSERVICEPROVIDERSServiceprovidersalsohaveasuiteofmethodsandconfigurationoptionsthatcanprovideadvancedfunctionalitytotheenduserwhentheproviderispublishedaspartofaComposerpackage.TakealookattheserviceproviderdefinitionintheLaravelsourcetolearnmoreabouthowthiscanwork.

Nowthatwe’vecoveredtheapplicationbootstrap,let’stakealookattheRequestobject,themostimportantoutputofthebootstrap.

Page 392: Laravel: Up and Running: A Framework for Building Modern PHP

TheRequestObjectTheIlluminateRequestclassisaLaravel-specificextensionofSymfony’sHttpFoundation\Requestobject.

SYMFONYHTTPFOUNDATION

Ifyou’renotfamiliarwithit,Symfony’sHttpFoundationsuiteofclassespowersalmosteveryPHPframeworkinexistenceatthispoint;thisisthemostpopularandpowerfulsetofabstractionsavailableinPHPforrepresentingHTTPrequests,responses,headers,cookies,andmore.

EachRequestobjectisintendedtorepresenteveryrelevantpieceofinformationyoucouldcaretoknowaboutauser ’sHTTPrequest.

InnativePHPcode,youmightfindyourselflookingto$_SERVER,$_GET,$_POST,andothercombinationsofglobalsandprocessinglogictogetinformationaboutthecurrentuser ’srequest.Whatfileshastheuseruploaded?What’shisIPaddress?Whatfieldsdidhepost?Allofthisissprinkledaroundthelanguage—andyourcode—inawaythat’shardtounderstandandhardertomock.

Symfony’sRequestobjectinsteadcollectsalloftheinformationnecessarytorepresentasingleHTTPrequestintoasingleobject,andthentacksonconveniencemethodstomakeiteasytogetusefulinformationfromit.TheIlluminateRequestobjectaddsevenmoreconveniencemethodstogetinformationabouttherequestit’srepresenting.

Page 393: Laravel: Up and Running: A Framework for Building Modern PHP
Page 394: Laravel: Up and Running: A Framework for Building Modern PHP

CAPTURINGAREQUESTYou’llverylikelyneverneedtodothisinaLaravelapp,butifyoueverneedtocaptureyourownIlluminateRequestdirectlyfromPHP’sglobals,youcanusethecapture()method:

$request=Illuminate\Http\Request::capture();

Page 395: Laravel: Up and Running: A Framework for Building Modern PHP

GettingaRequestObjectinLaravelRealistically,you’renotgoingtobecapturingyourownrequests.Laraveldoesthisforyouinitsbootstrap,andthereareafewwaysyoucangetaccesstoit.

First—andagain,we’llcoverthismoreinChapter11—youcantypehinttheclassinanyconstructorormethodthat’sresolvedbythecontainer.Thatmeansyoucantypehintitinacontrollermethodoraserviceprovider,asseeninExample10-2.

Example10-2.Typehintinginacontainer-resolvedmethodtoreceiveaRequestobject...

useIlluminate\Http\Request;

classPeopleControllerextendsController

{

publicfunctionindex(Request$request)

{

$allInput=$request->all();

}

Youcanalsousetherequest()globalhelper,whichallowsyoutocallmethodsonit(e.g.,request()->input())andalsoallowsyoutocallitonitsowntogetaninstanceof$request:

$request=request();

$allInput=request()->all();

Andyoucanalsousetheapp()globalmethodtogetaninstanceofRequest.Youcanpasseitherthefullyqualifiedclassnameortheshortcut,request:

$request=app(Illuminate\Http\Request::class);

$request=app('request');

Page 396: Laravel: Up and Running: A Framework for Building Modern PHP

GettingBasicInformationAboutaRequestNowthatweknowhowtogetaninstanceofRequest,whatcanwedowithit?TheprimarypurposeoftheRequestobjectistorepresentthecurrentHTTPrequest,sotheprimaryfunctionalitytheRequestclassoffersistomakeiteasytogetusefulinformationaboutthecurrentrequest.

I’vecategorizedthemethodsdescribedhere,butnotethatthere’scertainlyoverlapbetweenthecategories,andthecategoriesareabitarbitrary—forexample,queryparameterscouldjustaseasilybein“Userandrequeststate”astheyarein“Basicuserinput.”Hopefullythesecategorieswillmakeiteasyforyoutolearn,andthenyoucanthrowawaythecategories.

Also,beawarethattherearemanymoremethodsavailableontheRequestobject;thesearejustthemostcommonlyusedmethods.

BasicuserinputThebasicuserinputmethodsmakeitsimpletogetinformationthattheusersthemselvesexplicitlyprovide—likelythroughsubmittingaformoranAjaxcomponent.WhenIreference“user-providedinput”here,I’mtalkingaboutinputfromquerystrings(GET),formsubmissions(POST),orJSON:

all()returnsanarrayofalluser-providedinput.

input(fieldName)returnsthevalueofasingleuser-providedinputfield.

only(fieldName|[array,of,field,names])returnsanarrayofalluser-providedinputforthespecifiedfieldname(s).

except(fieldName|[array,of,field,names])returnsanarrayofalluser-providedinputexceptforthespecifiedfieldname(s).

exists(fieldName)returnsabooleanofwhetherornotthefieldexistsintheinput.

has(fieldName)returnsabooleanofwhetherthefieldexistsintheinputandisnotempty(hasavalue).

json()returnsaParameterBagifthepagehadJSONsenttoit.

json(keyName)returnsthevalueofthegivenkeyfromJSONsenttothepage.

Page 397: Laravel: Up and Running: A Framework for Building Modern PHP

PARAMETERBAG

SometimesinLaravelyou’llrunintoaParameterBag.Thisclassissortoflikeanassociativearray.Youcangetaparticularkeyusingget():

echo$bag->get('name');

Youcanalsousehas()tocheckfortheexistenceofakey,all()togetanarrayofallkeysandvalues,count()tocountthenumberofitems,andkeys()togetanarrayofjustthekeys.

Example10-3givesafewquickexamplesofhowtousetheuser-providedinformationmethodsfromarequest.

Example10-3.Gettingbasicuser-providedinformationfromtherequest//form

<formmethod="POST"action="/form">

{{csrf_field()}}

<inputname="name">Name<br>

<inputtype="submit">

</form>

//routereceivingtheform

Route::post('form',function(Request$request){

echo'nameis'.$request->input('name');

echo'allinputis'.print_r($request->all());

echo'userprovidedemailaddress:'.$request->has('email')?'true':'false';

});

UserandrequeststateTheuserandrequeststatemethodsincludeinputthatwasn’texplicitlyprovidedbytheuserthroughaform:

method()returnsthemethod(GET,POST,PATCH,etc.)usedtoaccessthisroute.

path()returnsthepath(withoutthedomain)usedtoaccessthispage;e.g.,forhttp://www.myapp.com/abc/defitwouldreturnabc/def.

url()returnstheURL(withthedomain)usedtoaccessthispage;e.g.,forhttp://www.myapp.com/abcitwouldreturnhttp://www.myapp.com/abc.

is()returnsabooleanofwhetherornotthecurrentpagerequestfuzzy-matchesaprovidedstring(e.g.,/a/b/cwouldbematchedby$request->is('*b*'),where*standsforanycharacters).ItusesacustomregexparserfoundinStr::is.

ip()returnstheuser ’sIPaddress.

header()returnsanarrayofheaders(e.g.,['accept-language'=>['en-US,en;q=0.8']]),or,ifpassedaheadernameasaparameter,returnsjustthatheader.

server()returnsanarrayofthevariablestraditionallystoredin$_SERVER(e.g.,REMOTE_ADDR),or,ifpasseda$_SERVERvariablename,returnsjustthatvalue.

Page 398: Laravel: Up and Running: A Framework for Building Modern PHP

secure()returnsabooleanofwhetherthispagewasloadedusingHTTPS.

pjax()returnsabooleanofwhetherthispagerequestwasloadedusingPjax.

wantsJson()returnsabooleanofwhetherthisrequesthasany/jsoncontenttypesinitsAcceptheaders.

isJson()returnsabooleanofwhetherthispagerequesthasany/jsoncontenttypesinitsContent-Typeheader.

accepts()returnsabooleanofwhetherthispagerequestacceptsagivencontenttype.

FilesSofar,alloftheinputwe’vecoverediseitherexplicit(retrievedbymethodslikeall(),input(),etc.)ordefinedbythebrowserorreferringsite(retrievedbymethodslikepjax()).Fileinputsaresimilartoexplicituserinput,butthey’rehandledmuchdifferently:

file()returnsanarrayofalluploadedfiles,or,ifakeyispassed(thefileuploadfieldname),returnsjusttheonefile.

hasFile()returnsabooleanofwhetherafilewasuploadedatthespecifiedkey.

Everyfilethat’suploadedwillbeaninstanceofSymfony\Component\HttpFoundation\File\UploadedFile,whichprovidesasuiteoftoolsforvalidating,processing,andstoringuploadedfiles.

TakealookatChapter14formoreexamplesofhowtohandleuploadedfiles.

Page 399: Laravel: Up and Running: A Framework for Building Modern PHP

PersistenceTherequestcanalsoprovidefunctionalityforinteractingwiththesession.Mostsessionfunctionalityliveselsewhere,butthereareafewmethodsthatareparticularlyrelevanttothecurrentpagerequest:

flash()flashesthecurrentrequest’suserinputtothesessiontoberetrievedlater.

flashOnly()flashesthecurrentrequest’suserinputforanykeysintheprovidedarray.

flashExcept()flashesthecurrentrequests’suserinput,exceptforanykeysintheprovidedarray.

old()returnsanarrayofallpreviouslyflasheduserinput,or,ifpassedakey,returnsthevalueforthatkeyifitwaspreviouslyflashed.

flush()wipesallpreviouslyflasheduserinput.

cookie()retrievesallcookiesfromtherequest,or,ifakeyisprovided,retrievesjustthatcookie.

hasCookie()returnsabooleanofwhethertherequesthasacookieforthegivenkey.

Theflash*()andold()methodsareusedforstoringuserinputandretrievingitlater,oftenaftertheinputisvalidatedandrejected.

Page 400: Laravel: Up and Running: A Framework for Building Modern PHP

TheResponseObjectSimilartotheRequestobject,there’sanIlluminateResponseobjectthatrepresentstheresponseyourapplicationissendingtotheenduser,completewithheaders,cookies,content,andanythingelseusedforsendingtheenduser ’sbrowserinstructionsonrenderingapage.

JustlikeRequest,theIlluminate\Http\ResponseobjectextendsaSymfonyclass:Symfony\Component\HttpFoundation\Response.Thisisabaseclasswithaseriesofpropertiesandmethodsthatmakeitpossibletorepresentandrenderaresponse;Illuminate’sResponseclassdecoratesitwithafewhelpfulshortcuts.

Page 401: Laravel: Up and Running: A Framework for Building Modern PHP

UsingandCreatingResponseObjectsinControllersBeforewetalkabouthowyoucancustomizeyourresponseobjects,let’sstepbackandseehowwemostcommonlyworkwithresponseobjects.

Intheend,anyresponseobjectreturnedfromaroutedefinitionwillbeconvertedintoanHTTPresponse.Itmaydefinespecificheadersorspecificcontent,setcookies,orwhateverelse,buteventuallyitwillbeconvertedintoaresponseyourusers’browserscanparse.

Let’stakealookatthesimplestpossibleresponse,inExample10-4.

Example10-4.SimplestpossibleHTTPresponseRoute::get('route',function(){

returnnewIlluminate\Http\Response('Hello!');

});

//Same,usingglobalfunction:

Route::get('route',function(){

returnresponse('Hello!');

});

Wecreatearesponse,giveitsomecoredata,andthenreturnit.WecanalsocustomizetheHTTPstatus,headers,cookies,andmore,likeinExample10-5.

Example10-5.SimpleHTTPresponsewithcustomizedstatusandheadersRoute::get('route',function(){

returnresponse('Error!',400)

->header('X-Header-Name','header-value')

->cookie('cookie-name','cookie-value');

});

SettingheadersWedefineaheaderonaresponsebyusingtheheader()fluentmethod,likeinExample10-5.Thefirstparameteristheheadernameandthesecondistheheadervalue.

AddingcookiesWecanalsosetcookiesdirectlyontheresponseobjectifwe’dlike.We’llcoverLaravel’scookiehandlingabitmoreinChapter14,buttakealookatExample10-6forasimpleusecaseforattachingcookiestoaresponse.

Example10-6.Attachingacookietoaresponsereturnresponse($content)

->cookie('signup_dismissed',true);

Page 402: Laravel: Up and Running: A Framework for Building Modern PHP

SpecializedResponseTypesTherearealsoafewspecialresponsetypesforviews,downloads,files,andJSON.Eachisapredefinedmacrothatmakesiteasytoreuseparticulartemplatesforheadersorcontentstructure.

ViewresponsesInChapter4,Iusedtheglobalview()helpertoshowhowtoreturnatemplate—forexample,view(view.name.here)orsomethingsimilar.Butifyouneedtocustomizeheaders,HTTPstatus,oranythingelsewhenreturningaview,youcanusetheview()responsetypeasshowninExample10-7.

Example10-7.Usingtheview()responsetypeRoute::get('/',function(XmlGetterService$xml){

$data=$xml->get();

returnresponse()

->view('xml-structure',$data)

->header('Content-Type','text/xml');

});

DownloadresponsesSometimesyouwantyourapplicationtoforcetheuser ’sbrowsertodownloadafile,whetheryou’recreatingthefileinLaravelorservingitfromadatabaseoraprotectedlocation.Thedownload()responsetypemakesthissimple.

Therequiredfirstparameteristhepathforthefileyouwantthebrowsertodownload.Ifit’sageneratedfile,you’llneedtosaveitsomewheretemporarily.

Theoptionalsecondparameteristhefilenameforthedownloadedfile(e.g.,export.csv).Ifyoudon’tpassastringhere,itwillbeautomaticallygenerated.Theoptionalthirdparameterallowsyoutopassanarrayofheaders.Example10-8illustratestheuseofthedownload()responsetype.

Example10-8.Usingthedownload()responsetypepublicfunctionexport()

{

returnresponse()

->download('file.csv','export.csv',['header'=>'value']);

}

publicfunctionotherExport()

{

returnresponse()->download('file.pdf');

}

FileresponsesThefileresponseissimilartothedownloadresponse,exceptitallowsthebrowsertodisplaythefileinsteadofforcingadownload.ThisismostcommonwithimagesandPDFs.

Therequiredfirstparameteristhefilename,andtheoptionalsecondparametercanbean

Page 403: Laravel: Up and Running: A Framework for Building Modern PHP

arrayofheaders(seeExample10-9).

Example10-9.Usingthefile()responsetypepublicfunctioninvoice($id)

{

returnresponse()->file("./invoices/{$id}.pdf",['header'=>'value']);

}

JSONresponsesJSONresponsesaresocommonthat,eventhoughthey’renotreallyparticularlycomplextoprogram,there’sacustomresponseforthemaswell.

JSONresponsesconvertthepasseddatatoJSON(withjson_encode())andsettheContent-Typetoapplication/json.YoucanalsooptionallyusethesetCallback()methodtocreateaJSONPresponseinsteadofJSON,asseeninExample10-10.

Example10-10.Usingthejson()responsetypepublicfunctioncontacts()

{

returnresponse()->json(Contact::all());

}

publicfunctionjsonpContacts(Request$request)

{

returnresponse()

->json(Contact::all())

->setCallback($request->input('callback'));

}

publicfunctionnonEloquentContacts()

{

returnresponse()->json(['Tom','Jerry']);

}

RedirectresponsesRedirectsaren’tcommonlycalledontheresponse()helper,sothey’reabitdifferentfromtheothercustomresponsetypeswediscussedalready,butthey’restilljustadifferentsortofresponse.Redirects,returnedfromaLaravelroute,sendtheuseraredirect(oftena301)toanotherpageorbacktothepreviouspage.

Youtechnicallycancallaredirectfromresponse(),asinreturnresponse()->redirectTo('/').Butmorecommonlyyou’llusetheredirect-specificglobalhelpers.

Thereisaglobalredirect()functionthatcanbeusedtocreateredirectresponses,andaglobalback()functionthatisashortcuttoredirect()->back().

Justlikemostglobalhelpers,theredirect()globalfunctioncaneitherbepassedparametersorcanbeusedtogetaninstanceofitsclassthatyouthenchainmethodcallsonto.Ifyoudon’tchain,butjustpassparameters,redirect()performsthesameasredirect()->to();ittakesastringandredirectstothatstringURL.Example10-11showssomeexamplesofitsuse.

Example10-11.Examplesofusingtheredirect()globalhelperreturnredirect('account/payment');

Page 404: Laravel: Up and Running: A Framework for Building Modern PHP

returnredirect()->to('account/payment');

returnredirect()->route('account.payment');

returnredirect()->action('AccountController@showPayment');

//Ifnamedrouteorcontrollerneedsparameters:

returnredirect()->route('contacts.edit',['id'=>15]);

returnredirect()->action('ContactsController@edit',['id'=>15]);

Youcanalsoredirect“back”tothepreviouspage,whichisespeciallyusefulwhenhandlingandvalidatinguserinput.Example10-12showsacommonpatterninvalidationcontexts.

Example10-12.Redirectbackwithinputpublicfunctionstore()

{

//Ifvalidationfails...

returnback()->withInput();

}

Finally,youcanredirectandflashdatatothesessionatthesametime.Thisiscommonwitherrorandsuccessmessages,likeinExample10-13.

Example10-13.RedirectwithflasheddataRoute::post('contacts',function(){

//storethecontact...

returnredirect('dashboard')->with('message','Contactcreated!');

});

Route::get('dashboard',function(){

//Gettheflasheddatafromsession--usuallyhandledinBladetemplate

echosession('message');

});

CustomresponsemacrosYoucanalsocreateyourowncustomresponsetypesusing“macros”.Thisallowsyoutodefineaseriesofmodificationstomaketotheresponseanditsprovidedcontent.

Let’sre-createthejson()customresponsetype,justtoseehowitworks.Asalways,youshouldprobablycreateacustomserviceproviderforthesesortsofbindings,butfornowwe’lljustputitinAppServiceProvider,asseeninExample10-14.

Example10-14.Creatingacustomresponsemacro...

classAppServiceProvider

{

publicfunctionboot()

{

Response::macro('myJson',function($content){

returnresponse(json_encode($content))

->headers(['Content-Type'=>'application/json']);

});

}

Then,wecanuseitjustlikewewouldusethepredefinedjsonmacro:

returnresponse()->myJson(['name'=>'Sangeetha']);

ThiswillreturnaresponsewiththebodyofthatarrayencodedforJSON,withtheJSON-

Page 405: Laravel: Up and Running: A Framework for Building Modern PHP

appropriateContent-Typeheader.

Page 406: Laravel: Up and Running: A Framework for Building Modern PHP

LaravelandMiddlewareTakealookbackatFigure10-1,atthestartofthischapter.

We’vecoveredtherequestsandresponses,butwehaven’tactuallylookedintowhatmiddlewareis.Youmayalreadybefamiliarwithmiddleware;it’snotuniquetoLaravel,butratherawidelyusedarchitecturepattern.

Page 407: Laravel: Up and Running: A Framework for Building Modern PHP

AnIntroductiontoMiddlewareTheideaofmiddlewareisthatthereisaseriesoflayerswrappingaroundyourapplication,likeamultilayercakeoranonion.JustasshowninExample10-1,everyrequestpassesthrougheverymiddlewarelayeronitswayintotheapplication,andthentheresultingresponsepassesbackthroughthemiddlewarelayersonitswayouttotheenduser.

Middlewareismostoftenconsideredseparatefromyourapplicationlogic,andusuallyisconstructedinawaythatshouldtheoreticallybeapplicabletoanyapplication,notjusttheoneyou’reworkingonatthemoment.

Middlewarecaninspectarequestanddecorateit,orrejectit,basedonwhatitfinds.Thatmeansmiddlewareisgreatforsomethinglikeratelimiting:itcaninspecttheIPaddress,checkhowmanytimesit’saccessedthisresourceinthelastminute,andsendbacka429(TooManyRequests)statusifathresholdispassed.

Becausemiddlewarealsogetsaccesstotheresponseonitswayoutoftheapplication,it’sgreatfordecoratingresponses.Forexample,Laravelusesamiddlewaretoaddallofthequeuedcookiesfromagivenrequest/responsecycletotheresponserightbeforeitissenttotheenduser.

Butsomeofthemostpowerfulusesofmiddlewarecomefromthefactthatitcanbenearlythefirstandthelastthingtointeractwiththerequest/responsecycle.Thatmakesitperfectforsomethinglikeenablingsessions—PHPneedsyoutoopenthesessionveryearlyandcloseitverylate,andmiddlewareisgreatforthis.

Page 408: Laravel: Up and Running: A Framework for Building Modern PHP

CreatingCustomMiddlewareLet’simaginewewanttohaveamiddlewarethatrejectseveryrequestthatusestheDELETEHTTPmethod,andalsosendsacookiebackforeveryrequest.

There’sanArtisancommandtocreatecustommiddleware.Let’stryitout:

phpartisanmake:middlewareBanDeleteMethod

Youcannowopenupthefileatapp/Http/Middleware/BanDeleteMethod.php.ThedefaultcontentsareshowninExample10-15.

Example10-15.Defaultmiddlewarecontents...

classBanDeleteMethod

{

publicfunctionhandle($request,Closure$next)

{

return$next($request);

}

}

Howthishandle()methodrepresentstheprocessingofboththeincomingrequestandtheoutgoingresponseisthemostdifficultthingtounderstandaboutmiddleware,solet’swalkthroughit.

Understandingmiddleware’shandle()methodFirst,rememberthatmiddlewarearelayeredoneontopofanother,andthenfinallyontopoftheapp.Thefirstmiddlewarethat’sregisteredgetsfirstaccesstoarequestwhenitcomesin,thenthatrequestispassedtoeveryothermiddlewareinturn,thentotheapp;thentheresultingresponseispassedoutwardthroughthemiddleware,andfinallythisfirstmiddlewaregetslastaccesstotheresponsewhenitgoesout.

Let’simaginewe’veregisteredBanDeleteMethodasthefirstmiddlewaretorun.Thatmeansthe$requestcomingintoitistherawrequest,unadulteratedbyanyothermiddleware.Nowwhat?

Passingthatrequestto$next()meanshandingitofftotherestofthemiddleware.The$next()closurejusttakesthat$requestandpassesittothehandle()methodofthenextmiddlewareinthestack.Itthengetspassedondownthelineuntiltherearenomoremiddlewaretohanditto,anditfinallyendsupattheapplication.

Next,howdoestheresponsecomeout?Thisiswhereitmightbehardtofollow.Theapplicationreturnsaresponse,whichispassedbackupthechainofmiddleware—becauseeachmiddlewarereturnsitsresponse.So,withinthatsamehandle()method,themiddlewarecandecoratea$requestandpassittothe$next()closure,andcanthenchoosetodosomethingwiththeoutputitreceivesbeforefinallyreturningthatoutputtotheenduser.Let’slookatsomepseudocodetomakethisclearer(Example10-16).

Page 409: Laravel: Up and Running: A Framework for Building Modern PHP

Example10-16.Pseudocodeexplainingthemiddlewarecallprocess...

classBanDeleteMethod

{

publicfunctionhandle($request,Closure$next)

{

//Atthispoint,$requestistherawrequestfromtheuser.

//Let'sdosomethingwithit,justforfun.

if($request->ip()==='192.168.1.1'){

returnresponse('BANNEDIPADDRESS!',403);

}

//Nowwe'vedecidedtoacceptit.Let'spassitontothenext

//middlewareinthestack.Wepassitto$next(),andwhatis

//returnedistheresponseafterthe$requesthasbeenpassed

//downthestackofmiddlewaretotheapplicationandthe

//application'sresponsehasbeenpassedbackupthestack.

$response=$next($request);

//Atthispoint,wecanonceagaininteractwiththeresponse

//justbeforeitisreturnedtotheuser

$response->cookie('visited-our-site',true);

//Finally,wecanreleasethisresponsetotheenduser

return$response;

}

}

Finally,let’smakethemiddlewaredowhatweactuallypromised(Example10-17).

Example10-17.Samplemiddlewarebanningthedeletemethod...

classBanDeleteMethod

{

publicfunctionhandle($request,Closure$next)

{

//TestfortheDELETEmethod

if($request->method()==='DELETE'){

returnresponse(

"Getoutofherewiththatdeletemethod",

405

);

}

$response=$next($request);

//Assigncookie

$response->cookie('visited-our-site',true);

//Returnresponse

return$response;

}

}

Page 410: Laravel: Up and Running: A Framework for Building Modern PHP

BindingMiddlewareWe’renotquitedoneyet.Weneedtoregisterthismiddlewareinoneoftwoways:globallyorforspecificroutes.

Globalmiddlewareareappliedtoeveryroute;routemiddlewareareappliedonaroute-by-routebasis.

BindingglobalmiddlewareBothbindingshappeninapp/Http/Kernel.php.Toaddamiddlewareasglobal,additsclassnametothe$middlewareproperty,asinExample10-18.

Example10-18.Bindingglobalmiddleware//app/Http/Kernel.php

protected$middleware=[

\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,

\App\Http\Middleware\BanDeleteMethod::class,

];

BindingroutemiddlewareMiddlewareintendedforspecificroutescanbeaddedasaroutemiddlewareoraspartofamiddlewaregroup.Let’sstartwiththeformer.

Routemiddlewareareaddedtothe$routeMiddlewarearrayinapp/Http/Kernel.php.It’ssimilartoaddingthemto$middleware,exceptwehavetogiveoneakeythatwillbeusedwhenapplyingthismiddlewaretoaparticularroute,asseeninExample10-19.

Example10-19.Bindingroutemiddleware//app/Http/Kernel.php

protected$routeMiddleware=[

'auth'=>\App\Http\Middleware\Authenticate::class,

...

'nodelete'=>\App\Http\Middleware\BanDeleteMethod::class,

];

Wecannowusethismiddlewareinourroutedefinitions,likeinExample10-20.

Example10-20.Applyingroutemiddlewareinroutedefinitions//Doesn'tmakemuchsenseforourcurrentexample...

Route::get('contacts',[

'middleware'=>'nodelete',

'uses'=>'ContactsController@index'

]);

//Makesmoresenseforourcurrentexample...

Route::group(['prefix'=>'api','middleware'=>'nodelete',function(){

//AllroutesrelatedtoanAPI

}]);

UsingmiddlewaregroupsLaravel5.2introducedtheconceptofmiddlewaregroups.They’reessentiallypre-packagedbundlesofmiddlewarethatmakesensetobetogetherinspecificcontexts.

Page 411: Laravel: Up and Running: A Framework for Building Modern PHP
Page 412: Laravel: Up and Running: A Framework for Building Modern PHP

MIDDLEWAREGROUPSIN5.2Thedefaultroutesfileinearlierreleasesof5.2,routes.php,hadthreedistinctsections:therootroute(/)wasn’tunderanymiddlewaregroup,andthentherewasawebmiddlewaregroupandanapimiddlewaregroup.Itwasabitconfusingfornewusers,andthatmeanttherootroutedidn’thaveaccesstothesessionoranythingelsethat’skickedoffinthemiddleware.

Inlaterversionsof5.2everything’ssimplified:everyrouteinroutes.phpisinthewebmiddlewaregroup.In5.3,yougetaroutes/web.phpfileforwebroutesandaroutes/api.phpfileforAPIroutes.Ifyouwanttoaddroutesinothergroups,readon.

Outoftheboxtherearetwogroups:webandapi.webhasallthemiddlewarethatwillbeusefulonalmosteveryLaravelpagerequest,includingmiddlewareforcookies,sessions,CSRFprotection,andmore.apihasnoneofthose—ithasathrottlemiddlewareandaroutemodelbindingmiddleware,andthat’sit.Thesearealldefinedinapp/Http/Kernel.php.

Youcanapplymiddlewaregroupstoroutesjustlikeyouapplyroutemiddlewaretoroutes,withthemiddleware()fluentmethod:

Route::get('/','HomeController@index')->middleware('web');

Youcanalsocreateyourownmiddlewaregroupsandaddandremoveroutemiddlewaretoandfrompreexistingmiddlewaregroups.Itworksjustlikeaddingroutemiddlewarenormally,butyou’reinsteadaddingthemtokeyedgroupsinthe$middlewareGroupsarray.

Youmightbewonderinghowthesemiddlewaregroupsmatchupwiththetwodefaultroutesfiles.Unsurprisingly,theroutes/web.phpfileiswrappedwiththewebmiddlewaregroup,andtheroutes/api.phpfileiswrappedwiththeapimiddlewaregroup.

Theroutes/*filesareloadedintheRouteServiceProvider.Takealookatthemap()methodthere(Example10-21)andyou’llfindamapWebRoutes()andamapApiRoutes()method,eachofwhichloadsitsrespectivefilesalreadywrappedintheappropriatemiddlewaregroup.

Example10-21.DefaultrouteserviceproviderinLaravel5.3//App\Providers\RouteServiceProvider

publicfunctionmap()

{

$this->mapApiRoutes();

$this->mapWebRoutes();

}

protectedfunctionmapApiRoutes()

{

Route::group([

'middleware'=>'api',

'namespace'=>$this->namespace,

'prefix'=>'api',

],function($router){

requirebase_path('routes/api.php');

});

}

protectedfunctionmapWebRoutes()

Page 413: Laravel: Up and Running: A Framework for Building Modern PHP

{

Route::group([

'middleware'=>'web',

'namespace'=>$this->namespace,

],function($router){

requirebase_path('routes/web.php');

});

}

AsyoucanseeinExample10-21,we’reusingtheroutertoloadaroutegroupunderthedefaultnamespace(App\Http\Controllers)andwiththewebmiddlewaregroup,andanotherundertheapimiddlewaregroup.

Page 414: Laravel: Up and Running: A Framework for Building Modern PHP

PassingParameterstoMiddlewareIt’snotcommon,buttherearetimeswhenyouneedtopassparameterstoaroutemiddleware.Forexample,youmighthaveanauthenticationmiddlewarethatwillactdifferentlydependingonwhetheryou’reguardingforthememberusertypeortheownerusertype:

Route::get('company',function(){

returnview('company.admin');

})->middleware('auth:owner');

Tomakethiswork,you’llneedtoaddoneormoreparameterstothemiddleware’shandle()method,andupdatethatmethod’slogicaccordingly:

publicfunctionhandle($request,$next,$role)

{

if(auth()->check()&&auth()->user()->hasRole($role)){

return$next($request);

}

returnredirect('login');

}

Notethatyoucanalsoaddmorethanoneparametertothehandle()method,andpassmultipleparameterstotheroutedefinitionbyseparatingthemwithcommas:

Route::get('company',function(){

returnview('company.admin');

})->middleware('auth:owner,view');

FORMREQUESTOBJECTS

InthischapterwecoveredhowtoinjectanIlluminateRequestobject,whichisthebase—andmostcommon—requestobject.

However,youcanalsoextendtheRequestobjectandinjectthatinstead.You’lllearnmoreabouthowtobindandinjectcustomclassesinChapter11,butthere’sonespecialtype,calledtheformrequest,thathasitsownsetofbehaviors.

See“FormRequests”tolearnmoreaboutcreatingandusingformrequests.

Page 415: Laravel: Up and Running: A Framework for Building Modern PHP

TestingOutsideofthecontextofyouasadeveloperusingrequests,responses,andmiddlewareinyourowntesting,Laravelitselfactuallyuseseachquiteabit.

Whenyou’redoingapplicationtesting—callslike$this->visit('/'),clicks,andwhateverelse—you’reinstructingLaravel’sapplicationtestingframeworktogeneraterequestobjectsthatrepresenttheinteractionsthatyou’redescribing.Thenthoserequestobjectsarepassedtoyourapplicationasifitwereanactualvisit.That’swhytheapplicationtestsaresoaccurate:yourapplicationdoesn’tactually“know”thatit’snotarealuserthat’sinteractingwithit.

Inthiscontext,manyoftheassertionsyou’remaking—say,assertResponseOk()—areassertionsagainsttheresponseobjectgeneratedbytheapplicationtestingframework.TheassertResponseOk()methodjustlooksattheresponseobjectandassertsthatitsisOk()methodreturnstrue—whichisjustcheckingthatitsstatuscodeis200.Intheend,everythinginapplicationtestingisactingasifthiswerearealpagerequest.

Findyourselfinacontextwhereyouneedarequesttoworkwithinyourtests?Youcanalwayspullonefromthecontainerwith$request=request().Oryoucouldcreateyourown—theconstructorparametersfortheRequestclass,alloptional,areasfollows:

$request=newIlluminate\Http\Request(

$query,//GETarray

$request,//POSTarray

$attributes,//"attributes"array;emptyisfine

$cookies,//cookiesarray

$files,//filesarray

$server,//serversarray

$content//rawbodydata

);

Ifyou’rereallyinterestedinanexample,checkoutthemethodSymfonyusestocreateanewRequestfromtheglobalsPHPprovides:Symfony\Component\HttpFoundation\Request@createFromGlobals().

Responsesareevensimplertocreatemanually,ifyouneedto.Herearethe(optional)parameters:

$response=newIlluminate\Http\Response(

$content,//responsecontent

$status,//HTTPstatus,default200

$headers//arrayheadersarray

);

Finally,ifyouneedtodisableyourmiddlewareduringanapplicationtest,importtheWithoutMiddlewaretraitintothattest.

Page 416: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DREveryrequestcomingintoaLaravelapplicationisconvertedintoanIlluminateRequestobject,whichthenpassesthroughallmiddleware,andisprocessedbytheapplication.Theapplicationgeneratesaresponseobject,whichisthenpassedbackthroughallofthemiddleware(inreverseorder)andreturnedtotheenduser.

Requestandresponseobjectsareresponsibleforencapsulatingandrepresentingeveryrelevantpieceofinformationabouttheincominguserrequestandtheoutgoingserverresponse.

Serviceproviderscollecttogetherrelatedbehaviorforbindingandregisteringclassesforusebytheapplication.

Middlewarewraptheapplicationandcanrejectordecorateanyrequestandresponse.

Page 417: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter11.TheContainer

Laravel’sservicecontainer,ordependencyinjectioncontainer,sitsatthecoreofalmosteveryotherfeature.Thecontainerisasimpletoolyoucanusetobindandresolveconcreteinstancesofclassesandinterfaces,andatthesametimeit’sapowerfulandnuancedmanagerofanetworkofinterrelateddependencies.Inthischapter,we’lllearnmoreaboutwhatitis,howitworks,andhowyoucanuseit.

Page 418: Laravel: Up and Running: A Framework for Building Modern PHP
Page 419: Laravel: Up and Running: A Framework for Building Modern PHP

NAMINGANDTHECONTAINERYou’llnoticeinthisbook,inthedocumentation,andinothereducationalsourcesthattherearequiteafewnamesfolksuseforthecontainer.Theseinclude:

Applicationcontainer

IoC(inversionofcontrol)container

Servicecontainer

DI(dependencyinjection)container

Allareusefulandvalid,butjustknowthey’realltalkingaboutthesamething.They’reallreferringtotheservicecontainer.

Page 420: Laravel: Up and Running: A Framework for Building Modern PHP

AQuickIntroductiontoDependencyInjectionDependencyinjectionmeansthat,ratherthanbeinginstantiated(“newedup”)withinaclass,eachclass’sdependencieswillbeinjectedinfromtheoutside.Thismostcommonlyoccurswithconstructorinjection,whichmeansanobject’sdependenciesareinjectedwhenit’screated.Butthere’salsosetterinjection,wheretheclassexposesamethodspecificallyforinjectingagivendependency,andmethodinjection,whereoneormoremethodsexpecttheirdependenciestobeinjectedwhenthey’recalled.

TakealookatExample11-1foraquickexampleofconstructorinjection,themostcommontypeofdependencyinjection.

Example11-1.Basicdependencyinjection<?php

classUserMailer

{

protected$mailer;

publicfunction__construct(Mailer$mailer)

{

$this->mailer=$mailer;

}

publicfunctionwelcome($user)

{

return$this->mailer->mail($user->email,'Welcome!');

}

}

Asyoucansee,thisUserMailerclassexpectsanobjectoftypeMailertobeinjectedwhenit’sinstantiated,anditsmethodsthenrefertothatinstance.

Theprimarybenefitsofdependencyinjectionarethatitgivesusthefreedomtochangewhatwe’reinjecting,mockdependenciesfortesting,andinstantiateshareddependenciesjustonceforshareduse.

INVERSIONOFCONTROL

Youmayhaveheardthephrase“inversionofcontrol”usedinconjunctionwith“dependencyinjection,”andsometimesLaravel’scontaineriscalledtheIoCcontainer.

Thetwoconceptsareverysimilar.Inversionofcontrolreferencestheideathat,intraditionalprogramming,thelowest-levelcode—specificclasses,instances,andproceduralcode—“controls”whichinstanceofaparticularpatternorinterfacetouse.Forexample,ifyou’reinstantiatingyourmailerineachclassthatneedsit,eachclassgetstodecidewhethertouseMailgunorMandrillorSendgrid.

Theideaofinversionofcontrolreferstoflippingthat“control”toliveattheoppositeendofyourapplication.Nowthedefinitionofwhichmailertouselivesatthehighest,mostabstractlevelofyourapplication,ofteninconfiguration.Everyinstance,everypieceoflow-levelcode,looksuptothehigh-levelconfigurationtoessentially“ask”:“Canyougivemeamailer?”Theydon’t“know”whichmailerthey’regetting,justthatthey’regettingone.

DependencyinjectionandespeciallyDIcontainersprovideagreatopportunityforinversionofcontrol,becauseyoucandefineoncewhichconcreteinstanceoftheMailerinterface,forexample,toprovidewheninjectingmailersintoanyclassthatneedsthem.

Page 421: Laravel: Up and Running: A Framework for Building Modern PHP

DependencyInjectionandLaravelAswesawinExample11-1,themostcommonpatternfordependencyinjectionisconstructorinjection,orinjectingthedependenciesofanobjectwhenit’sinstantiated(“constructed”).

Let’stakeourUserMailerclassfromExample11-1.Example11-2showswhatitmightlookliketocreateanduseaninstanceofit.

Example11-2.Simplemanualdependencyinjection$mailer=newMailgunMailer($mailgunKey,$mailgunSecret,$mailgunOptions);

$userMailer=newUserMailer($mailer);

$userMailer->welcome($user);

Nowlet’simaginewewantourUserMailerclasstobeabletologmessages,aswellassendinganotificationtoaSlackchanneleverytimeitsendsamessage.Example11-3showswhatthiswouldlooklike.Asyoucansee,itwouldstarttogetprettyunwieldyifwehadtodoallthisworkeverytimewewantedtocreateanewinstance—especiallywhenyouconsiderthatwe’llhavetogetalltheseparametersfromsomewhere.

Example11-3.Morecomplexmanualdependencyinjection$mailer=newMailgunMailer($mailgunKey,$mailgunSecret,$mailgunOptions);

$logger=newLogger($logPath,$minimumLogLevel);

$slack=newSlack($slackKey,$slackSecret,$channelName,$channelIcon);

$userMailer=newUserMailer($mailer,$logger,$slack);

$userMailer->welcome($user);

ImaginehavingtowritethatcodeeverytimeyouwantedaUserMailer.Dependencyinjectionisgreat,butthisisamess.

Page 422: Laravel: Up and Running: A Framework for Building Modern PHP

Theapp()GlobalHelperBeforewegotoofarintohowthecontaineractuallyworks,let’stakeaquicklookatthesimplestwaytogetanobjectoutofthecontainer:theapp()helper.

Passanystringtothathelper,whetherit’safullyqualifiedclassname(FQCN)oraLaravelshortcut(we’lltalkaboutthosemoreinasecond),andit’llreturnaninstanceofthatclass:

$logger=app(Logger::class);

Thisistheabsolutesimplestwaytointeractwiththecontainer.Itcreatesaninstanceofthisclassandreturnsitforyou.Niceandeasy.

DIFFERENTSYNTAXESFOR MAKINGACONCRETEINSTANCE

Thesimplestwayto“make”aconcreteinstanceistousetheglobalhelperandpasstheclassorinterfacenamedirectlytothehelper,usingapp('FQCN').

However,ifyouhaveaninstanceofthecontainer—whetheritwasinjectedsomewhere,orifyou’reinaserviceproviderandusing$this->app,or(alesser-knowntrick)ifyougetonebyjustrunning$container=app()—thereareafewwaystomakeaninstancefromthere.

Themostcommonwayistorunthemake()method.$app->make('FQCN')workswell.However,youmayalsoseeotherdevelopersandthedocumentationusethissyntaxsometimes:$app['FQCN'].Don’tworry.That’sdoingthesamething;it’sjustadifferentwayofwritingit.

CreatingtheLoggerinstanceasshownhereseemssimpleenough,butyoumight’venoticedthatour$loggerclassinExample11-3hastwoparameters:$logPathand$minimumLogLevel.Howdoesthecontainerknowwhattopasshere?

Shortanswer:itdoesn’t.Youcanusetheapp()globalhelpertocreateaninstanceofaclassthathasnoparametersinitsconstructor,butatthatpointyoucould’vejustrunnewLoggeryourself.Thecontainershineswhenthere’ssomecomplexityintheconstructor,andthat’swhenweneedtolookathowexactlythecontainercanfigureouthowtoconstructclasseswithconstructorparameters.

Page 423: Laravel: Up and Running: A Framework for Building Modern PHP

HowtheContainerIsWiredBeforewedigfurtherintotheLoggerclass,takealookatExample11-4.

Example11-4.LaravelautowiringclassBar

{

publicfunction__construct(){}

}

classBaz

{

publicfunction__construct(){}

}

classFoo

{

publicfunction__construct(Bar$bar,Baz$baz){}

}

$foo=app(Foo::class);

ThislookssimilartoourmailerexampleinExample11-3.What’sdifferentisthatthesedependencies(BarandBaz)arebothsosimplethatthecontainercanresolvethemwithoutanyfurtherinformation.Thecontainerreadsthetypehintsintheconstructor,resolvesaninstanceofeach,andtheninjectsthemintothenewFooinstancewhenit’screatingit.Thisiscalledautowiring:resolvinginstancesbasedontype-hintswithoutthedeveloperneedingtoexplicitlybindthoseclassesinthecontainer.

Page 424: Laravel: Up and Running: A Framework for Building Modern PHP
Page 425: Laravel: Up and Running: A Framework for Building Modern PHP

TYPEHINTSINPHP“Typehinting”inPHPmeansputtingthenameofaclassorinterfaceinfrontofavariableinamethodsignature:

publicfunction__construct(Logger$logger){}

ThistypehintistellingPHPthatwhateverispassedintothemethodmustbeoftypeLogger,whichcouldbeeitheraninterfaceoraclass.

Autowiringmeansthat,ifaclasshasnotbeenexplicitlyboundtothecontainer(likeFoo,Bar,orBazinthiscontext)butthecontainercanfigureouthowtoresolveitanyway,thecontainerwillresolveit.Thismeansanyclasswithnoconstructordependencies(likeBarandBaz)andanyclasswithconstructordependenciesthatthecontainercanresolve(likeFoo)canberesolvedoutofthecontainer.

Thatleavesusonlyneedingtobindclassesthathaveunresolvableconstructorparameters—forexample,our$loggerclassinExample11-3,whichhasparametersrelatedtoourlogpathandloglevel.

Forthose,we’llneedtolearnhowtoexplicitlybindsomethingtothecontainer.

Page 426: Laravel: Up and Running: A Framework for Building Modern PHP

BindingClassestotheContainerBindingaclasstoLaravel’scontainerisessentiallytellingthecontainer,“IfadeveloperasksforaninstanceofLogger,here’sthecodetoruninordertoinstantiateonewiththecorrectparametersanddependenciesandthenreturnitcorrectly.”

We’reteachingthecontainerthat,whensomeoneasksforthisparticularstring(whichisusuallytheFQCNofaclass),itshouldresolveitthisway.

Page 427: Laravel: Up and Running: A Framework for Building Modern PHP

BindingtoaClosureSo,let’slookathowtobindtothecontainer.Notethattheappropriateplacetobindtothecontainerisinaserviceprovider ’sregister()method(seeExample11-5).

Example11-5.Basiccontainerbinding//Inserviceprovider

publicfunctionregister()

{

$this->app->bind(Logger::class,function($app){

returnnewLogger('\log\path\here','error');

});

}

Thereareafewimportantthingstonoteinthisexample.First,we’rerunning$this->app->bind().$this->appisaninstanceofthecontainerthat’salwaysavailableoneveryserviceprovider.Thecontainer ’sbind()methodiswhatweusetobindtothecontainer.

Thefirstparameterofbind()isthe“key”we’rebindingto.Herewe’veusedtheFQCNoftheclass.Thesecondparameterdiffersdependingonwhatyou’redoing,butessentiallyitshouldbesomethingthatshowsthecontainerwhattodotoresolveaninstanceofthatboundkey.

So,inthisexample,we’repassingaclosure.Andnow,anytimesomeonerunsapp(Logger::class),they’llgettheresultofthisclosure.Theclosureispassedaninstanceofthecontaineritself($app),soiftheclassyou’reresolvinghasadependencyyouwantresolvedoutofthecontainer,youcanuseitinyourdefinition:

$this->app->bind(UserMailer::class,function($app){

returnnewUserMailer(

$app->make(Mailer::class),

$app->make(Logger::class),

$app->make(Slack::class)

);

});

Notethateverytimeyouaskforanewinstanceofyourclass,thisclosurewillberunagainandthenewoutputreturned.

Page 428: Laravel: Up and Running: A Framework for Building Modern PHP

BindingtoSingletons,Aliases,andInstancesIfyouwanttheoutputofthebindingclosuretobecachedsothatthisclosureisn’tre-runeverytimeyouaskforaninstance,that’stheSingletonpattern,andyoucanrun$this->app->singleton()todothat:

publicfunctionregister()

{

$this->app->singleton(Logger::class,function(){

returnnewLogger('\log\path\here','error');

});

}

Youcanalsogetsimilarbehaviorifyoualreadyhaveaninstanceoftheobjectyouwantthesingletontoreturn:

publicfunctionregister()

{

$logger=newLogger('\log\path\here','error');

$this->app->instance(Logger::class,$logger);

}

Finally,ifyouwanttoaliasoneclasstoanother,bindaclasstoashortcut,orbindashortcuttoaclass,youcanjustpasstwostrings:

$this->bind(Logger::class,FirstLogger::class);

//or

$this->bind('log',FirstLogger::class);

//or

$this->bind(FirstLogger::class,'log');

NotethattheseshortcutsarecommoninLaravel’score;itprovidesasystemofshortcutstoclassesthatprovidecorefunctionality,usingeasy-to-rememberkeyslikelog.

Page 429: Laravel: Up and Running: A Framework for Building Modern PHP

BindingaConcreteInstancetoanInterfaceJustlikewecanbindaclasstoanotherclass,oraclasstoashortcut,wecanalsobindtoaninterface.Thisisextremelypowerful,becausewecannowtypehintinterfacesinsteadofclassnames,likeinExample11-6.

Example11-6.Typehintingandbindingtoaninterface...

useInterfaces\Mailer;

classUserMailer

{

protected$mailer;

publicfunction__construct(Mailer$mailer)

{

$this->mailer=$mailer;

}

}

//serviceprovider

publicfunctionregister()

{

$this->app->bind(\Interfaces\Mailer::class,function(){

returnnewMailgunMailer(...);

});

}

YoucannowtypehintMailerorLoggerinterfacesallacrossyourcode,andthenchooseonceinaserviceproviderwhichspecificmailerorloggeryouwanttouseeverywhere.That’sinversionofcontrol.

Page 430: Laravel: Up and Running: A Framework for Building Modern PHP

ContextualBindingSometimesyouneedtochangehowtoresolveaninterfacedependingonthecontext.Youmightwanttologeventsfromoneplacetoalocalsyslogandfromothersouttoanexternalservice.So,let’stellthecontainertodifferentiate—checkoutExample11-7.

Example11-7.Contextualbinding//Inaserviceprovider

publicfunctionregister()

{

$this->app->when(FileWrangler::class)

->needs(Interfaces\Logger::class)

->give(Loggers\Syslog::class);

$this->app->when(Jobs\SendWelcomeEmail::class)

->needs(Interfaces\Logger::class)

->give(Loggers\PaperTrail::class);

}

Page 431: Laravel: Up and Running: A Framework for Building Modern PHP

ConstructorInjectionWe’vecoveredtheconceptofconstructorinjection,andwe’velookedathowthecontainermakesiteasytoresolveinstancesofaclassorinterfaceoutofthecontainer.Wesawhoweasyitistousetheapp()helpertomakeinstances,andalsohowthecontainerwillresolvetheconstructordependenciesofaclasswhenit’screatingit.

Whatwehaven’tcoveredyetishowthecontainerisalsoresponsibleforresolvingmanyofthecoreoperatingclassesofyourapplication.Forexample,everycontrollerisinstantiatedbythecontainer.Thatmeansifyouwantaninstanceofaloggerinyourcontroller,youcansimplytypehinttheloggerclassinyourcontroller ’sconstructor,andwhenLaravelcreatesthecontroller,itwillresolveitoutofthecontainerandthatloggerinstancewillbeavailabletoyourcontroller.TakealookatExample11-8foranexample.

Example11-8.Injectingdependenciesintoacontroller...

classMyControllerextendsController

{

protected$logger;

publicfunction__construct(Logger$logger)

{

$this->logger=$logger;

}

publicfunctionindex()

{

//Dosomething

$this->logger->error('Somethinghappened');

}

}

Thecontainerisresponsibleforresolvingcontrollers,middleware,queuejobs,eventlisteners,andanyotherclassesthatareautomaticallygeneratedbyLaravelintheprocessofyourapplication’slifecycle—soanyofthoseclassescantypehintdependenciesintheirconstructorsandexpectthemtobeautomaticallyinjected.

Page 432: Laravel: Up and Running: A Framework for Building Modern PHP

MethodInjectionThereareafewplacesinyourapplicationwhereLaraveldoesn’tjustreadtheconstructorsignature:italsoreadsthemethodsignatureandwillinjectdependenciesforyouthereaswell.

Themostcommonplacetousemethodinjectionisincontrollermethods.Ifyouhaveadependencyyouonlywanttouseforasinglecontrollermethod,youcaninjectitintojustthatmethodlikeinExample11-9.

Example11-9.Injectingdependenciesintoacontrollermethod...

classMyControllerextendsController

{

//Methoddependenciescancomeafterorbeforerouteparameters

publicfunctionshow(Logger$logger,$id)

{

//Dosomething

$logger->error('Somethinghappened');

}

}

Thisisalsoavailableontheboot()methodofserviceproviders,andyoucanalsoarbitrarilycallamethodonanyclassusingthecontainer,whichwillallowformethodinjectionthere(seeExample11-10).

Example11-10.Manuallycallingaclassmethodusingthecontainer’scall()methodclassFoo

{

publicfunctionbar($parameter1){}

}

$foo=newFoo;

//Callsthe'bar'methodon$foowithafirstparameterof"value"

app()->call($foo,'bar',['parameter1'=>'value']);

Page 433: Laravel: Up and Running: A Framework for Building Modern PHP

FacadesandtheContainerWe’vecoveredfacadesquiteabitsofarinthebook,butwehaven’tactuallytalkedabouthowtheywork.

Laravel’sfacadesareclassesthatprovidesimpleaccesstocorepiecesofLaravel’sfunctionality.Therearetwotrademarkfeaturesoffacades:first,they’reallavailableintheglobalnamespace(\Logisanaliasto\Illuminate\Support\Facades\Log),andsecond,theyusestaticmethodstoaccessnonstaticresources.

Let’stakealookattheLogfacade,sincewe’vebeenlookingatloggingalreadyinthischapter.Inyourcontrollerorviewsyoucouldusethiscall:

Log::alert('Somethinghasgonewrong!');

Here’swhatitwouldlookliketomakethatsamecallwithoutthefacade:

$logger=app('log');

$logger->alert('Somethinghasgonewrong!');

Asyoucansee,facadestranslatestaticcalls(anymethodcallthatyoumakeonaclassitself,using::,insteadofonaninstance)tonormalmethodcallsoninstances.

Page 434: Laravel: Up and Running: A Framework for Building Modern PHP
Page 435: Laravel: Up and Running: A Framework for Building Modern PHP

IMPORTINGFACADENAMESPACESIfyou’reinanamespacedclass,you’llwanttobesuretoimportthefacadeatthetop:

...

useIlluminate\Support\Facades\Log;

classControllerextendsController

{

publicfunctionindex()

{

//...

Log::error('Somethingwentwrong!');

}

Page 436: Laravel: Up and Running: A Framework for Building Modern PHP

HowFacadesWorkSo,let’stakealookattheLogfacadeandseehowitactuallyworks.

First,openuptheclassIlluminate\Support\Facades\Log.You’llseesomethinglikeExample11-11.

Example11-11.TheLogfacadeclass<?php

namespaceIlluminate\Support\Facades;

classLogextendsfacade

{

protectedstaticfunctiongetFacadeAccessor()

{

return'log';

}

}

Everyfacadehasasinglemethod:getFacadeAccessor().ThisdefinesthekeythatLaravelshouldusetolookupthisfacade’sbackinginstancefromthecontainer.

Inthisinstance,wecanseethateverycalltotheLogfacadeisproxiedtobeacalltoaninstanceofthelogshortcutfromthecontainer.Ofcourse,that’snotarealclassorinterfacename,soweknowit’soneofthoseshortcutsImentionedearlier.

So,here’swhat’sreallyhappening:

Log::error('Help!');

//isthesameas...

app('log')->error('Help!');

Thereareafewwaystolookupexactlywhatclasseachfacadeaccessorpointsto,butcheckingthedocumentationistheeasiest.There’satableonthefacadesdocumentationpagethatshowsyou,foreachfacade,whichcontainerbinding(shortcut,likelog)it’sconnectedto,andwhichclassthatreturns.Itlookslikethis:

Facade Class ServiceContainerBinding

App Illuminate\Foundation\Application app

… … …

Log Illuminate\Log\Writer log

Nowthatyouhavethisreference,youcandothreethings.

First,youcanalwaysfigureoutwhatmethodsareavailableonafacade.Justfinditsbackingclassandlookatthedefinitionofthatclass,andyou’llknowthatanyofitspublicmethodsarecallableonthefacade.

Page 437: Laravel: Up and Running: A Framework for Building Modern PHP

Second,youcanfigureouthowtoinjectafacade’sbackingclassusingdependencyinjection.Ifyoueverwantthefunctionalityofafacadebutprefertousedependencyinjection,justtypehintthefacade’sbackingclassorgetaninstanceofitwithapp()andcallthesamemethodsyouwould’vecalledonthefacade.

Third,youcanseehowtocreateyourownfacades.CreateaclassforthefacadethatextendsIlluminate\Support\Facades\Facade,andgiveitagetFacadeAccessor()method,whichreturnsastring.Makethatstringsomethingthatcanbeusedtoresolveyourbackingclassoutofthecontainer—maybejusttheFQCNoftheclass.Finally,youhavetoregisterthefacadebyaddingittothealiasesarrayinconfig/app.php.Done!Youjustmadeyourownfacade.

Page 438: Laravel: Up and Running: A Framework for Building Modern PHP

ServiceProvidersWe’vecoveredthebasicsofserviceprovidersinthepreviouschapter(see“ServiceProviders”).What’smostimportantwithregardtothecontaineristhatyouremembertoregisteryourbindingsintheregister()methodofsomeserviceprovidersomewhere.

YoucanjustdumploosebindingsintoApp\Providers\AppServiceProvider,whichisabitofacatchall,butit’sgenerallybetterpracticetocreateauniqueserviceproviderforeachgroupoffunctionalityyou’redeveloping,andbinditsclassesinitsuniqueregister()method.

Page 439: Laravel: Up and Running: A Framework for Building Modern PHP

TestingTheabilitytouseinversionofcontrolanddependencyinjectionmakestestinginLaravelextremelyversatile.Youcanbindadifferentlogger,forinstance,dependingonwhethertheappisliveorundertesting.OryoucanchangethetransactionalemailservicefromMailguntoalocalemailloggerforeasyinspection.Bothoftheseswapsareactuallysocommonthatit’seveneasiertomakethemusingLaravel’s.envconfigurationfiles,butyoucanmakesimilarswapswithanyinterfacesorclassesyou’dlike.

Theeasiestwaytodothisistoexplicitlyre-bindclassesandinterfaceswhenyouneedthemrebound,directlyinthetest.Example11-12showshow.

Example11-12.Overridingabindingintestspublicfunctiontest_it_does_something()

{

app()->bind(Interfaces\Logger,function(){

returnnewDevNullLogger;

});

//dostuff

}

Ifyouneedcertainclassesorinterfacesreboundgloballyforyourtests(whichisnotaparticularlycommonoccurrence),youcandothiseitherinthetestclass’ssetUp()methodorinLaravel’sTestCasebasetest’ssetUp()method,asinExample11-13.

Example11-13.OverridingabindingforalltestsclassTestCaseextends\Illuminate\Foundation\Testing\TestCase

{

publicfunctionsetUp()

{

parent::setUp();

app()->bind('whatever','whateverelse');

}

}

WhenusingsomethinglikeMockery,it’scommontocreateamockorspyorstubofaclass,andthenre-bindthattothecontainerinplaceofitsreferent.

Page 440: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRLaravel’sservicecontainerhasmanynames,butintheenditsgoalistomakeiteasytodefinehowtoresolvecertainstringnamesasconcreteinstances.Thesestringnamesaregoingtobethefullyqualifiedclassnamesofclassesorinterfaces,orshortcutslikelog.

Eachbindingteachestheapplication,givenastringkey(e.g.,app('log')),howtoresolveaconcreteinstance.

Thecontainerissmartenoughtodorecursivedependencyresolution,soifyoutrytoresolveaninstanceofsomethingthathasconstructordependencies,thecontainerwilltrytoresolvethosedependenciesbasedontheirtypehints,thenpassthemintoyourclass,andfinallyreturnaninstance.

Thereareafewwaystobindtothecontainer,butintheendtheyalldefinewhattoreturngivenaparticularstring.

Facadesaresimpleshortcutsthatmakeiteasytousestaticcallsonaroot-namespacedclasstocallnonstaticmethodsonclassesresolvedoutofthecontainer.

Page 441: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter12.Testing

MostdevelopersknowthattestingyourcodeisAGoodThing.We’resupposedtodoit.Welikelyhaveanideaofwhyit’sgood,andwemight’veevenreadsometutorialsabouthowit’ssupposedtowork.

Butthegapbetweenknowingwhyyoushouldtestandknowinghowtotestiswide.Thankfully,toolslikePHPUnit,Mockery,andPHPSpechaveprovidedanincrediblenumberofoptionsfortestinginPHP—butitcanstillbeprettyoverwhelmingtogeteverythingsetup.

Outofthebox,Laravelcomeswithbaked-inintegrationstoPHPUnit(unittesting),Behat(behavior-drivendevelopment),Mockery(mocking),andFaker(creatingfakedataforseedingandtesting).Italsocomeswithitsownsimpleandpowerfulsuiteofapplicationtestingtools,whichallowyouto“crawl”yoursite’sURIs,clickbuttons,submitforms,checkHTTPstatuscodes,andvalidateandassertagainstJSON.

Laravel’stestingsetupevenhasasampleapplicationtestthatcanrunsuccessfullythemomentyoucreateanewapp.Thatmeansyoudon’thavetospendanytimeconfiguringyourtestingenvironment,andthat’sonelessbarriertowritingyourtests.

TESTINGTERMS

It’shardtogetanygroupofprogrammerstoagreeonwhichtermstheyusetodefinedifferenttypesoftests.

Inthisbook,I’llusethreeprimaryterms:

UnittestsUnitteststargetsmall,relativelyisolatedunits—aclassormethod,usually.

IntegrationtestsIntegrationteststestthewayindividualunitsworktogetherandpassmessages.

ApplicationtestsOftencalledacceptanceorfunctionaltests,applicationteststesttheentirebehavioroftheapplication,usuallyatanouterboundarybyemployingsomethinglikeadocumentobjectmodel(DOM)crawler—whichisexactlywhatLaravel’sapplicationtestsuiteoffers.

Page 442: Laravel: Up and Running: A Framework for Building Modern PHP

TestingBasicsTestsinLaravelliveinthetestsfolder,andyoucanseetherearetwofilesintherebydefault:TestCase.php,whichisabaseclassintendedtobeextendedbyanyapplicationtests,andExampleTest.php,whichisaready-to-runapplicationtestthatwillreturngreenonanynewapp.

AsyoucanseeinExample12-1,ExampleTest“crawls”theDOMofthepagereturnedattherootpathofyourapplicationandchecksfortheword“Laravel.”Ifitfindsit,it’llpass;ifnot,it’llfail.

Example12-1.tests/ExampleTest.php<?php

useIlluminate\Foundation\Testing\WithoutMiddleware;

useIlluminate\Foundation\Testing\DatabaseMigrations;

useIlluminate\Foundation\Testing\DatabaseTransactions;

classExampleTestextendsTestCase

{

/**

*Abasicfunctionaltestexample.

*

*@returnvoid

*/

publicfunctiontestBasicExample()

{

$this->visit('/')

->see('Laravel');

}

}

Torunthistest,gotothecommandlineandrun./vendor/bin/phpunitfromtherootfolderofyourapplication.YoushouldseesomethingliketheoutputinExample12-2.

Example12-2.SampleExampleTestoutputPHPUnit5.5.2bySebastianBergmannandcontributors.

.

Time:139ms,Memory:12.00Mb

OK(1test,2assertions)

YoujustranyourfirstLaravelapplicationtest!Asyoucansee,you’resetupoutoftheboxnotonlywithafunctioningPHPUnitinstance,butalsoafull-fledgedapplicationtestingsuitecompletewithaDOMcrawler.

Incaseyou’renotfamiliarwithPHPUnit,let’schangethetesttolookfor“Applesauce,”likeinExample12-3,toseewhatanerrorlookslike.

Example12-3.tests/ExampleTest.php,editedtofailpublicfunctiontestBasicExample()

{

$this->visit('/')

->see('Applesauce');

}

Page 443: Laravel: Up and Running: A Framework for Building Modern PHP

Whoops!ThistimetheoutputwillprobablylookabitlikeExample12-4.

Example12-4.SamplefailingExampleTestoutputPHPUnit5.5.2bySebastianBergmannandcontributors.

F

Time:115ms,Memory:12.00Mb

Therewas1failure:

1)ExampleTest::testBasicExample

<sourceofpagehere>

FailedassertingthatthepagecontainstheHTML[Applesauce].

Pleasecheckthecontentabove.

/path-to-your-app/vendor/.../Foundation/Testing/Constraints/PageConstraint.php:90

/path-to-your-app/vendor/.../Foundation/Testing/Concerns/InteractsWithPages.php:271

/path-to-your-app/vendor/.../Foundation/Testing/Concerns/InteractsWithPages.php:287

/path-to-your-app/tests/ExampleTest.php:21

FAILURES!

Tests:1,Assertions:2,Failures:1.

Let’sbreakthisdown.First,wegetanFinsteadofa.uptop(justbelowthePHPUnitattributioninformation).Then,foreacherror,itshowsusthetestname(here,1)ExampleTest::testBasicExample),theerrormessage(Failedasserting...),andafullstacktraceofourerror,sowecanseewhatwascalled.Sincethiswasanapplicationtest,thestacktracejustshowsusthatitwascalledviatheInteractsWithPagestrait,butifthiswereaunitorapplicationtest,we’dseetheentirecallstackofthetest.

ASAMPLEJSONTEST

Asyoucanseeinthisexample,JSONtestingissimpleandclear—perhapssimplerthananyothersortofapplicationtesting:

publicfunctiontest_people_list_shows_person_after_creation()

{

$this->json('post','people',['name'=>'matt']);

$this->json('get','people');

$this->seeJson(['name'=>'matt']);

}

JustrunyourPOST,GET,DELETE,orwhateverelse,andthenassertthatthedatabase,oradditionalGETresponse,oranythingelsereturnswhatyouexpectafteryou’veperformedthegivenaction.

Let’slearnmoreaboutLaravel’stestingenvironment.

Page 444: Laravel: Up and Running: A Framework for Building Modern PHP

NamingTestsBydefault,Laravel’stestingsystemwillrunanyfilesinthetestsdirectorywhosenamesendwiththewordTest.That’swhytests/ExampleTest.phpwasrunbydefault.

Ifyou’renotfamiliarwithPHPUnit,youmightnotknowthatonlythemethodsinyourtestswithnamesthatstartwiththewordtestwillberun—ormethodswitha@testdocblock.SeeExample12-5forwhichmethodswillandwon’trun.

Example12-5.NamingPHPUnitmethodsclassNaming

{

publicfunctiontest_it_names_things_well()

{

//Runsas"testitnamesthingswell"

}

publicfunctiontestItNamesThingsWell()

{

//Runsas"Itnamesthingswell"

}

/**@test*/

publicfunctionit_names_things_well()

{

//Runsas"itnamesthingswell"

}

publicfunctionit_names_things_well()

{

//Doesn'trun

}

}

Page 445: Laravel: Up and Running: A Framework for Building Modern PHP

TheTestingEnvironmentAnytimeaLaravelapplicationisrunning,ithasacurrent“environment”namethatrepresentstheenvironmentit’srunningin.Thisnamemaybesettolocal,staging,production,oranythingelseyouwant.Youcanretrievethisbyrunningapp()->environment(),oryoucanrunsomethinglikeif(app()->environment('local'))totestwhetherthecurrentenvironmentmatchesthepassedname.

Whenyouruntests,Laravelautomaticallysetstheenvironmenttotesting.Thismeansyoucantestforif(app()->environment('testing'))toenableordisablecertainbehaviorsinthetestingenvironment.

Additionally,Laraveldoesn’tloadthenormalenvironmentvariablesfrom.envfortesting.Ifyouwanttosetanyenvironmentvariablesforyourtests,editphpunit.xmland,inthe<php>section,addanew<env>foreachenvironmentvariableyouwanttopassin—forexample,<envname="DB_CONNECTION"value="sqlite"/>.

USING.ENV.TESTTOEXCLUDETESTINGENVIRONMENTVARIABLESFROMVERSIONCONTROL

Ifyouwanttosetenvironmentvariablesforyourtest,youcandosoinphpunit.xmlasjustdescribed.Butwhatifyouhaveenvironmentvariablesforyourteststhatyouwanttobedifferentforeachtestingenvironment?Orwhatifyouwantthemtobeexcludedfromsourcecontrol?

Thankfully,handlingtheseconditionsisprettyeasy.First,createan.env.test.examplefile—justlikeLaravel’s.env.examplefile—andadd.env.testtoyour.gitignorefilejustbelow.env.Next,addthevariablesyou’dliketobeenvironment-specificto.env.test.example,justlikethey’resetin.env.example.Then,makeacopyof.env.test.exampleandnameit.env.test.

Finally,let’sloadthatfileintoourtests.Intests/TestCase.php,inthecreateApplication()method,pastethiscodejustbelowthe$app=require(...)line:

if(file_exists(dirname(__DIR__).'/.env.test')){

(new\Dotenv\Dotenv(dirname(__DIR__),'.env.test'))->load();

}

That’sit!You’renowloading.env.testtoprovideenvironmentvariablestoeverytest.

Page 446: Laravel: Up and Running: A Framework for Building Modern PHP

TheTestingTraitsBeforewegetintothemethodsyoucanusefortesting,you’llwanttoknowaboutthethreetestingtraitsyoucanpullintoanytestclass.

Page 447: Laravel: Up and Running: A Framework for Building Modern PHP

WithoutMiddlewareIfyouimportIlluminate\Foundation\Testing\WithoutMiddlewareintoyourtestclass,itwilldisableallmiddlewareforanytestinthatclass.Thismeansyouwon’thavetoworryabouttheauthenticationmiddleware,orCSRFprotection,oranythingelsethatmightbeusefulintherealapplicationbutdistractinginatest.

Page 448: Laravel: Up and Running: A Framework for Building Modern PHP

DatabaseMigrationsLaravelprovidestwotoolsoutoftheboxtokeepyourdatabaseintherightstatebetweentests:theDatabaseMigrationstraitandtheDatabaseTransactionstrait.

IfyouimporttheDatabaseMigrationstrait,itwillrunyourentiresetofdatabasemigrationsupbeforeeachtestanddownaftereachtest.LaravelmakesthishappenbyrunningphpartisanmigrateinthesetUp()methodbeforeeverytestrunsandphpartisanmigrate:rollbackinthetearDown()methodaftereachtestfinishes.

Page 449: Laravel: Up and Running: A Framework for Building Modern PHP

DatabaseTransactionsDatabaseTransactions,ontheotherhand,expectsyourdatabasetobeproperlymigratedbeforeyourtestsstart.Then,itwrapseverytestinadatabasetransaction,whichitrollsbackattheendofeachtest.Thismeansthat,attheendofeachtest,yourdatabasewillbereturnedtotheexactsamestateitwasinpriortothetest.

Page 450: Laravel: Up and Running: A Framework for Building Modern PHP

ApplicationTestingNowthatwe’velaidoutthebasicframeworkofLaravel’stestingenvironment,let’stakealookathowitactuallyworks.

InLaravel’sdefaultExampleTest(tests/ExampleTest.php)youcanseethat,withafewlinesofcode,wecan“crawl”toparticularURIsinourapplicationandactuallychecktheoutputforcertainwords.ButhowcanPHPUnitnavigatepagesasifitwereabrowser?

Page 451: Laravel: Up and Running: A Framework for Building Modern PHP

TestCaseAnyapplicationtestsshouldextendtheTestCaseclass(tests/TestCase.php)that’sincludedwithLaravelbydefault.Yourapplication’sTestCaseclasswillextendtheabstractIlluminate\Foundation\Testing\TestCaseclass,whichbringsinquiteafewgoodies.

ThefirstthingthetwoTestCaseclasses(yoursanditsabstractparent)doishandlebootingtheIlluminateapplicationinstanceforyou,soyouhaveafullybootstrappedapplicationavailable.Theyalso“refresh”theapplicationbetweeneachtest,whichmeansthey’renotentirelyre-creatingtheapplicationbetweentests,butrathermakingsureyoudon’thaveanydatalingering.

TheparentTestCasealsosetsupasystemofhooksthatallowcallbackstoberunbeforeandaftertheapplicationiscreated,andimportsaseriesoftraitsthatprovideyouwithmethodsforinteractingwitheveryaspectofyourapplication.ThesetraitsincludeInteractsWithContainer,MakesHttpRequests,InteractsWithConsole,andmore,andtheybringinabroadvarietyofcustomassertionsandtestingmethods.

Asaresult,yourapplicationtestshaveaccesstoafullybootstrappedapplicationinstance,application-test-mindedcustomassertions,andaDOMcrawler,withaseriesofsimpleandpowerfulwrappersaroundeachtomakethemeasytouse.

Thatmeansyoucanwrite$this->visit('/')->see('Laravel')andknowthatyourapplicationisactuallybehavingasifitwererespondingtoanormalHTTPrequest,andthattheresponseisbeingpassedtoaDOMcrawlerthatischeckingforthattextforyou.It’sprettypowerfulstuff,consideringhowlittleworkyouhadtodotogetitrunning.

So,let’slookatsomebasicmethodsthisopensuptoyou.

Page 452: Laravel: Up and Running: A Framework for Building Modern PHP
Page 453: Laravel: Up and Running: A Framework for Building Modern PHP

DIFFERENTTRAITSTRUCTUREINLARAVEL5.1InLaravel5.1,thestructureoftestingtraitsandhowthetestingframeworkisorganizedisverydifferentfromwhatI’vedescribedhere;however,thefunctionalityisstillthesame.

Page 454: Laravel: Up and Running: A Framework for Building Modern PHP

“Visiting”RoutesThemostcomplexofLaravel’sapplicationtestingfunctionalityisalsothesimplest—andmostpowerful—touse.Usingthesemethods,yourtestscaninteractwith(“visit”)pagesinyourapplicationlikeneverbefore:

$this->visit($uri)

VisitingarouteisatthecoreofLaravel’sapplicationtesting.Whenyoucall$this->visit('dashboard'),you’remimickingtheactiontheframeworktakeswhenawebrequestcomesinforthatsameroute.Theapplicationwillcreatearequestobjectforthatrequest,handleitlikenormal,andstoretheresponseobject(aninstanceofIlluminate\Http\Response)in$this->response.Thisisthesameresponseobjectthatwouldnormallybereturnedanddisplayedtothebrowser,butit’sjustcachedforLaravel’sapplicationtestingassertionstocheckagainst(orforyourcode,ifyouwanttointeractwiththeresponse).Onitsown,visitingdoesn’tdomuch,butnowthatyouhavearesponsecachedin$this->response,youcanwriteassertionsagainstit.

WHATMAKESVISIT( ) DIFFERENTFROMTHEOTHER VISITINGMETHODS

We’reabouttocovercall(),andget(),andmanyothermethodsrelatedtovisitingroutes.Butthey’remuchsimplerthanvisit(),andit’sworthseeingjustwhatmakesvisit()different.

Here’sashorteneddefinitionofthevisit()method:

publicfunctionvisit($uri)

{

$uri=$this->prepareUrlForRequest($uri);

$this->call($method,$uri,$parameters,$cookies,$files);

$this->clearInputs()->followRedirects()->assertPageLoaded($uri);

$this->currentUri=$this->app->make('request')->fullUrl();

$this->crawler=newCrawler(

$this->response->getContent(),

$this->currentUri

);

return$this;

}

Iknowthisisalottotakein,sojustsufficeittosayvisit()isdoingalot.Whenyouwanttocheckthatapageloads,whenyouwanttocrawlapage,whenyouwanttodoallthisnearlymagicalapplicationtesting,usevisit().

Ifyoujustwanttogetaresponseandnothingelse,orifyou’reusingmoretraditionalcheckstoPOSTtoapageandassertcertainbehaviorhappensorsomethingelse,you’llbefineusingthesimplermethodslikecall().

$this->call($method,$uri,$params=[],$cookies=[],$files=[],$server=[],$content=null)

Page 455: Laravel: Up and Running: A Framework for Building Modern PHP

IfyouneedtomakecallsagainsttheserverwithoutworryingaboutcrawlingthereturnedDOM—forexample,ifyouwanttoassertthatagivenPOSThascertaineffects—there’samethodforthat.visit()isactuallybasedoncall(),butyoucanalsousecall()directly.Asyoucanseefromthemethoddefinition,wehavealotofoptionsavailabletouswhenweusecall()—theHTTPmethod,theURI,parameters,cookies,andfiles,allpretendingtobesentalongwithourcall.Justlikewhenyouusevisit(),theserequestswillmakearequestandstoretheresponseon$this->response,buttheywon’tenableanyDOM-crawling-basedassertionslikesee().

$this->get($uri,$headers=[]),->post($uri,$data=[],$headers=[]),->put($uri,$data=[],$headers=[]),->patch(),and->delete()

Theseareaseriesofconveniencehelpersthatwrapcall();they’realljustshortcutstopassingaparticularstringtothefirstparameterofcall(),theHTTPmethod.Otherwise,youcanusethemexactlythesameasyouwouldcall().

$this->json($method,$uri,$data=[],$headers=[])Justlikeget(),post(),andtheothermethodsjustmentionedjson()isawrapperaroundcall().ItconvertsthepasseddatatoJSONandaddsJSONrequestheaders,andthenpassesitallintocall().json()isexceptionallyuseful,unsurprisingly,fortestingJSONAPIs.Becauseyoucanevendefineyourheadersanddata,youcanusethismethodtofullyinteractwithyourRESTAPIsinyourtests,likewesawin“ASampleJSONTest”.

$this->followRedirects()

There’sactuallyanotherthingthatvisit()doesthatcall()doesn’t:ittellsLaraveltofollowanyredirectsusingfollowRedirects(),andthenchecksthattheeventuallandingpageloadedusingassertPageLoaded().WithoutfollowRedirects(),theresponseyou’llgetaftercallingaredirectedpagewilljustbethecontentsoftheredirect,notthepagethatyouwerebeingredirectedto.

Page 456: Laravel: Up and Running: A Framework for Building Modern PHP

CustomApplicationTestingAssertionsSo,whatarethenewapplicationtestingassertionswe’vegained?Therearequiteafew.Let’sstartsimpleandmoveup:

$this->assertPageLoaded()

assertPageLoaded()checksthatyougotanHTTPstatuscodeof200whenloadingthepage.

$this->see()and->dontSee()Likewesawearlierinthischapter,see()takesastringandusesaregularexpressiontocheckthatthatstringispresentsomewhereonthepagethat’srendered.dontSee()isitsinverse.

$this->seeLink()and->dontSeeLink()seeLink()takestwoparameters:first,thelinktexttofind,andsecond,optionally,theURL.dontSeeLink()isitsinverse.

$this->seeHeader()

seeHeader()takestwoparameters:first,thenameoftheheader,andsecond,optionally,thevalueoftheheader.

$this->seeCookie()

seeCookie()takestwoparameters:first,thenameofthecookie,andsecond,optionally,thevalueofthecookie.

$this->seeInField()and->dontSeeInField()seeInField()takestwoparameters:first,thenameorIDoftheinputortextareatolookat,andsecond,thevaluetolookfor.dontSeeInField()isitsinverse.

$this->seeIsChecked()and->dontSeeIsChecked()seeIsChecked()takesoneparameter,thenameorIDofthecheckboxinputtoinspect.dontSeeIsChecked()isitsinverse.

$this->seeIsSelected()and->dontSeeIsSelected()seeIsSelected()takestwoparameters:first,thenameorIDoftheselectboxtoinspect,andsecond,thevaluetocheckwhetheritissetto.dontSeeIsSelected()isitsinverse.

$this->seePageIs()

seePageIs()assertsthatthecurrentloadedpageURIisthesameastheparameteryoupasstoit.

$this->seeInDatabase()and->dontSeeInDatabase()Tocheckforrecordsinthedatabasetable,passinthetablenameasthefirstparameterofseeInDatabase()andthedatayou’relookingforasthesecond:

Page 457: Laravel: Up and Running: A Framework for Building Modern PHP

publicfunctiontest_database_has_user_after_registration()

{

$this

->visit('register')

->fillForm([

'email'=>'[email protected]'

])

->submitForm();

$this->seeInDatabase('emails',['email'=>'[email protected]']);

}

Asyoucansee,thesecond“data”parameterofseeInDatabase()isstructuredlikeaSQLWHEREstatement—youpassakeyandavalue(ormultiplekeysandvalues),andthenLaravellooksforanyrecordsinthespecifieddatabasetablethatmatchyourkey(s)andvalue(s).Asalways,dontSeeInDatabase()istheinverse.

Page 458: Laravel: Up and Running: A Framework for Building Modern PHP

JSONandNon-visit()ApplicationTestingAssertionsTheremainingapplicationassertionsaretiedlesscloselytothevisit()methodologyandalittlemorecloselytotheimplementationdetailsofyourapplication.QuiteafewofthesearealsooftenusedfortestingJSONAPIs:

$this->seeJson(),->dontSeeJson(),->seeJsonEquals()seeJson()withnoparameterscheckstomakesurethatthecontentoftheresponsewasvalidJSON.Itsoptionalparameterrepresentsthedatathatyou’recheckingfor.Forinstance,inthefollowingexamplewereceivearesponse,andwe’recheckingboththatitisvalidJSONandthatitcontainsakey/valuepairofusername/mattstauffersomewhereinit:

publicfunctiontest_api_returns_certain_json()

{

$this->json('get','users');

$this->seeJson(['username'=>'mattstauffer']);

}

Asalways,seeJson()hasaninverse,dontSeeJson().dontSeeJson()stillexpectsvalidJSON,butitexpectstonotseeanythingpassedinasdata.Finally,ifyouwanttocheckthattheJSONmapsexactlytoyourdata,youcantryseeJsonEquals(),whichcomparesadataarraytotheJSONresponseandthrowsanexceptioniftheydon’tmatchexactly.

$this->assertResponseOK()and->assertResponseStatus($status)Afteranycall()orvisit(),onevaluableassertionisjustthatthepageloadedwiththeHTTPstatusyouexpected.assertResponseOK()assertsthatthepagereturneda200HTTPstatuscode,butyoucanalsopassaspecificstatusthatyouexpect:

publicfunctiontest_pages_load_the_way_we_want()

{

$this->get('people');

$this->assertResponseOK();

$this->call('post','owners');

$this->assertResponseStatus(405);//Methodnotallowed

}

Youcouldevenchecktheauthorizationsettingsforaparticularroutebyassertingthatitgivesa401(Unauthorized)statuscode,thenauthenticateandassertitgivesa200statuscode.

$this->assertViewHas($key,$value=null),->assertViewHasAll(array$bindings),->assertViewMissing($key)

Page 459: Laravel: Up and Running: A Framework for Building Modern PHP

Sometimestheonlyoptionyouhaveistoassertthatyouseeaparticularphraseonapageusingsee(),butwhat’smorelikelyisthatyou’rereallycheckingthatthecorrectdatawaspassedtoyourview.Thankfully,youcancheckthatdirectlywiththesemethods.assertViewHas()checksthatdatawithaparticularkeywassenttothemostrecentlyretrievedview,andifyoupasstheassertionasecondparameter,itwillassertthatthatdatawasequaltoit:

//Route

Route::get('test',function(){

returnview('test')->with('foo','bar');

});

//Test

publicfunctiontest_view_gets_data()

{

$this->get('test');

$this->assertViewHas('foo');//true

$this->assertViewHas('foo','bar');//true

$this->assertViewHas('foo','baz');//false

}

YoucanalsocheckformultipleviewvariablesatonceusingassertViewHasAll(),whichexpectsanarrayofkey/valuepairs:

//Route

Route::get('test',function(){

returnview('test')

->with('foo','bar')

->with('baz','qux');

});

//Test

publicfunctiontest_view_gets_data()

{

$this->get('test');

$this->assertViewHasAll([

'foo'=>'bar',

'baz'=>'qux'

]);//true

}

Youcanensurethattheviewhasn’tbeenpassedaparticularkeybypassingthatkeytoassertViewMissing().InLaravel5.3,asshowninthefollowingexample,youcanpassaclosuretothesecondparameterofassertViewHas().Thisgivesyoutheopportunitytoperformmuchmorenuancedchecksofthedatayourviewisprovided:

publicfunctiontest_events_are_owned_by_user_1()

{

$this->get('events');

$this->assertViewHas('events',function($events){

return$events->reject(function($event){

return$event->user_id===1;

})->isEmpty();

});

}

Page 460: Laravel: Up and Running: A Framework for Building Modern PHP

$this->assertRedirectedTo(),->assertRedirectedToRoute(),->assertRedirectedToAction()

Ifyouwanttoensurethattheusernotonlyendsupataparticularpagebutwassentthereasaredirect,thesemethodsprovidethatfunctionality.YoucancheckbyURL(to()),routename(toRoute()),orcontrollerandmethod(toAction()):

//Route

Route::get('redirector',function(){

returnredirect('/');

});

Route::get('/','HomeController@index')->name('home');

//Test

publicfunctiontest_redirector_works()

{

$this->get('redirector');

$this->assertRedirectedTo('/');

$this->assertRedirectedToRoute('home');

$this->assertRedirectedToAction('HomeController@index');

}

$this->assertSessionHas($key,$value=''),->assertSessionHasAll($bindings),->assertSessionHasErrors($bindings=[],$format=null),->assertHasOldInput()

Thesemethodsmakeiteasytocheckforspecificvaluesinthesession.assertSessionHas()andassertSessionHasAll()areshapedjustlikeassertViewHas()andassertViewHasAll().Whenpassedjustoneparameter,assertSessionHas()assertsthatthereisasessionvaluewiththatkey;ifyoupassittwoparameters,itassertsthatthevalueofthatsessionkeyisequaltothesessionparameter.assertSessionHasAll()takesanarrayofkey/valuepairsandassertsthateachkeyexistsinthesessionandissettoitscorrespondingvalue:

publicfunctiontest_session_has_stuff()

{

Session::put('foo','bar');

Session::put('baz','qux');

$this->assertSessionHas('foo');

$this->assertSessionHas('foo','bar');

$this->assertSessionHasAll([

'foo'=>'bar',

'baz'=>'qux'

]);

}

assertSessionHasErrors()withnoparametersassertsthatthere’satleastoneerrorsetinLaravel’sspecialerrorssessioncontainer.Itsfirstparametercanbeanarrayofkey/valuepairsthatdefinetheerrorsthatshouldbesetanditssecondparametercanbethestringformatthatthecheckederrorsshouldbeformattedagainst,asdemonstratedhere:

Page 461: Laravel: Up and Running: A Framework for Building Modern PHP

publicfunctiontest_posting_empty_errors_out()

{

//assumingthe"/form"routerequiresanemailfield,andwe're

//postinganemptyformtoittotriggertheerror

$this->post('form',[]);

$this->assertSessionHasErrors();

$this->assertSessionHasErrors(['email'=>'Theemailfieldisrequired.']);

$this->assertSessionHasErrors(

['email'=>'<p>Theemailfieldisrequired.</p>'],

'<p>:message</p>'

);

}

Finally,assertHasOldInput()assertsthatsomeoldinputhasbeensavedfromaformthatwassubmitted,likelyusingredirect()->withOldInput().

Page 462: Laravel: Up and Running: A Framework for Building Modern PHP

ClickingandFormsLet’smoveintosomemagicalyetterrifyingpowers:navigatingthroughforms,clickingandfillingandunchecking,andevenattachingfiles.Laravelprovidesthefollowingmethodsforworkingwithforms:

$this->click($name)

Givenalinkwiththeprovided$nameaseitherthebodyofthelinkoritsnameorID,click()grabstheURIfromthatlinkandvisitsit.

$this->type($text,$element)

Givenaninputonthepagewiththeprovided$elementasthenameorID,type()“types”theprovidedtextintoit.

MANIPULATINGFORMS

Withthistalkofclickinglinksandtypingintoformfields,itmayseemlikeLaravelisrunningsomesortofJavaScript-basedapplicationtestwhereit’sactuallydrivingabrowserinteractingwiththepage.Butit’snot,really.

It’sstoringupthis“input”you’recreating,andifatanypointyou“submit”theform,it’llgathertogetheryourinputandpostittothetargetoftheform.Intheoryit’sverydifferentfromtheuser’sexperience,butinpracticeit’sabeautifullyeloquentsyntaxforwritingteststhatmimicformsubmissions.

$this->check($element)

Givenacheckboxonthepagewiththeprovided$elementasthenameorID,check()“checks”it.

$this->uncheck($element)

Givenacheckboxonthepagewiththeprovided$elementasthenameorID,uncheck()“unchecks”it.

$this->select($option,$element)

Givenaselectboxonthepagewiththeprovided$elementasthenameorID,select()setsitsvalueto$option.

$this->attach($filePath,$element)

Givenafileuploadinputonthepagewiththeprovided$elementasthenameorID,attach()attachesafilefromthegivenlocalfilepathtoit,markedtouploadwhentheformissubmitted.

$this->press($buttonText)

Givenabuttononthepagewiththeprovidedtext,press()submitstheformthatbuttonisapartof.

$this->submitForm($buttonText,$inputs=[],$uploads=[])

Page 463: Laravel: Up and Running: A Framework for Building Modern PHP

Givenabuttononthepagewiththeprovidedtext,submitForm()submitstheformthatbuttonisapartof.Youcanalsooptionallysetoroverridealloftheinputsandfileuploadsusingthesecondandthirdparameters.

$this->fillForm($buttonText,$inputs=[])

Givenabuttononthepagewiththeprovidedtext,fillForm()findstheformthatbuttonisapartofandsetsallthevaluestobetheprovidedvalues.

$this->clearInputs()

clearInputs()wipesanyinputsoruploadsthathavebeenpreviouslyset.

Page 464: Laravel: Up and Running: A Framework for Building Modern PHP

JobsandEventsWe’llcoverthesejob-andevent-relatedtestsinmoredepthinChapter16,butlet’stakeaquicklookathowtheywork:

$this->expectsEvents($eventClassName)

Ifyouwanttoassertthataparticularclassofeventwasfiredduringyourtest,youcanpasstheclassnametoexpectsEvents():

publicfunctiontest_usersubscribed_event_fires_when_subscribing()

{

$this->expectsEvents(App\Events\UserSubscribed::class);

$this->visit('subscribe')->type('[email protected]','email')->press('Subscribe');

}

$this->withoutEvents()

withoutEvents()isnotactuallyanassertion;rather,itdisablesLaravel’seventhandlingsystemsothat,duringthistest,youdon’thavetoworryabouttheeffectsofanyofyoureventstakingplace—forexample,sendinganyemailsorwritinganylogs.

$this->expectsJobs()

Ifyouwanttoassertthataparticularclassofjobwasfiredduringyourtest,youcanpassthatclassnametoexpectsJobs():

publicfunctiontest_number_of_subscriptions_crunches_reports()

{

$this->expectsJobs(App\Jobs\CrunchReports::class);

$this->visit('subscribe')->type('[email protected]','email')->press('Subscribe');

}

Page 465: Laravel: Up and Running: A Framework for Building Modern PHP

AuthenticationandSessionsLaravelmakesitsimpletosetupatestenvironmentforyourtests,evenmakingiteasytocontrolthesessionandauthenticateasagivenuser:

$this->session(['key'=>'value'])

session()startsthesessionandsavesanykey/valuepairsofdataintheprovidedarraytothesession.Youcanrunthismultipletimesduringatesttoadddifferentpiecesofsessiondata,ifyou’dlike.

$this->flushSession()

flushSession()wipesallofthedatainthecurrentsession.

$this->be($authenticatable)

be()takesanyobjectthatfulfillstheIlluminate\Contracts\Auth\Authenticatableinterface(including,ofcourse,thebaseApp\Userclass)andauthenticateseverypagerequestorinteractioninthetestasthatuser.Thismeansyoucanwritetestslikethis:

publicfunctiontest_members_cant_see_admin_dashboard()

{

$member=factory(\App\User::class,'member')->create();

$this->be($member);

$this->get('admin-dashboard');

$this->assertResponseStatus(403);

}

publicfunctiontest_admins_can_see_admin_dashboard()

{

$admin=factory(\App\User::class,'admin')->create();

$this->be($admin);

$this->get('admin-dashboard');

$this->assertResponseOK();

}

Page 466: Laravel: Up and Running: A Framework for Building Modern PHP

ArtisanandSeedAlmostdone.Therearetwomoretestmethodsyoumightwanttotakealookat:

$this->artisan($command,$parameters=[])

IfyouwanttouseanArtisancommandinatest,artisan()makesiteasy.Justpassthecommandnameasthefirstparameterand,optionally,passanyparametersasanarrayasthesecond.Doingsowillsavetheresponsecodeto$this->codeincaseyou’dliketoassertagainstit,butitwillalsoreturnit.So,thisfunctionsthesameasArtisan::call()withtheadditionofsavingtheresponseto$this->code:

publicfunctiontest_returns_certain_code()

{

$this->artisan('do:thing',['--flagOfSomeSort'=>true]);

$this->assertEquals(0,$this->code);//0means"noerrorswerereturned"

}

$this->seed($seederClassName='DatabaseSeeder')

Ifyouwanttoseedyourdatabase,seed()willdothatforyou;andifyoupassanargument,youcanchoosetoonlyrunasingleseeder.seed()providesthesamefunctionalityasrunning$this->artisan('db:seed').

Page 467: Laravel: Up and Running: A Framework for Building Modern PHP
Page 468: Laravel: Up and Running: A Framework for Building Modern PHP

MODELFACTORIESModelfactoriesareamazingtoolsthatmakeiteasytoseedrandomized,well-structureddatabasedatafortesting(orotherpurposes).

We’vealreadycoveredthemindepth,socheckout“ModelFactories”tolearnmore.

Page 469: Laravel: Up and Running: A Framework for Building Modern PHP

MockingMocks(andtheirbrethren,spiesandstubsanddummiesandfakesandwhoknowswhatelse)arecommontoolsintesting.Iwon’tgointogreatdetailhere,butit’sunlikelyyoucanthoroughlytestanapplicationofanysizewithoutmockingatleastonethingoranother.

Essentially,mocksandothersimilartoolsmakeitpossibletocreateanobjectthatinsomewaymimicsarealclass,butfortestingpurposesisn’ttherealclass.Sometimesthisisdonebecausetherealclassistoodifficulttoinstantiatejusttoinjectitintoatest,ormaybetherealclasscommunicateswithanexternalservice.

Asyoucanprobablytellfromtheseexamples,Laravelencouragesworkingwiththerealapplicationasmuchaspossible—whichmeansavoidingtoogreatofadependenceonmocks.Buttheyhavetheirplace,whichiswhyLaravelincludesMockery,amockinglibrary,outofthebox.

Page 470: Laravel: Up and Running: A Framework for Building Modern PHP

MockeryMockeryallowsyoutoquicklyandeasilycreatemocksfromanyPHPclassinyourapplication.ImagineyouhaveaclassthatdependsonaSlackclient,butyoudon’twantthecallstoactuallygoouttoSlack.MockerymakesitsimpletocreateafakeSlackclienttouseinyourtests,likeyoucanseeinExample12-6.

Example12-6.UsingMockeryinLaravel//app/SlackClient.php

classSlackClient

{

...

publicfunctionsend($message,$channel)

{

//ActuallysendsamessagetoSlack

}

}

//app/Notifier.php

classNotifier

{

private$slack;

publicfunction__construct(SlackClient$slack)

{

$this->slack=$slack;

}

publicfunctionnotifyAdmins($message)

{

$this->slack->send($message,'admins');

}

}

//tests/NotifierTest.php

publicfunctiontest_notifier_notifies_admins()

{

$slackMock=Mockery::mock(SlackClient::class)->shouldIgnoreMissing();

$notifier=newNotifier($slackMock);

$notifier->notifyAdmins('Testmessage');

}

Therearealotofmovingpieceshere,butlet’sbreakitdown.WehaveaclassnamedNotifierthatwe’retesting.IthasadependencynamedSlackClientthatdoessomethingthatwedon’twantittodowhenwe’rerunningourtests:itsendsactualSlacknotifications.Sowe’regoingtomockit.

WeuseMockerytogetamockofourSlackClientclass.Ifwedon’tcareaboutwhathappenstothatclass—ifitshouldsimplyexisttokeepourtestsfromthrowingerrors—wecanjustuseshouldIgnoreMissing():

$slackMock=Mockery::mock(SlackClient::class)-shouldIgnoreMissing();

NomatterwhatNotifiercallson$slackMock,it’lljustacceptitandreturnnull.

Buttakealookattest_notifier_notifies_admins().Atthispoint,itdoesn’tactuallytestanything.

Page 471: Laravel: Up and Running: A Framework for Building Modern PHP

WecouldjustkeepshouldIgnoreMissing()andthenwritesomeassertionsbelowit.That’susuallywhatwedowithshouldIgnoreMissing(),whichmakesthisobjecta“fake”ora“stub.”

Butwhatifwewanttoactuallyassertthatacallwasmadetothesend()methodofSlackClient?That’swhenwedropshouldIgnoreMissing()andreachfortheshould*methods(Example12-7).

Example12-7.UsingtheshouldReceivemethodonaMockerymockpublicfunctiontest_notifier_notifies_admins()

{

$slackMock=Mockery::mock(SlackClient::class);

$slackMock->shouldReceive('send')->once();

$notifier=newNotifier($slackMock);

$notifier->notifyAdmins('Testmessage');

}

shouldReceive('send')->once()isthesameassaying“assertthat$slackMockwillhaveitssend()methodcalledonceandonlyonce.”So,we’renowassertingthatNotifier,whenwecallnotifyAdmins(),mustmakeasinglecalltothesendmethodonSlackClient.

WecouldalsousesomethinglikeshouldReceive('send')->times(3)orshouldReceive('send')->never().

WhatifwewantedtousetheIoCcontainertoresolveourinstanceoftheNotifier?ThismightbeusefulifNotifierhadseveralotherdependenciesthatwedidn’tneedtomock.

Wecandothat!Wejustusetheinstance()methodonthecontainer,asinExample12-8,totellLaraveltoprovideaninstanceofourmocktoanyclassesthatrequestit(which,inthisexample,willbeNotifier).

Example12-8.BindingaMockeryinstancetothecontainerpublicfunctiontest_notifier_notifies_admins()

{

$slackMock=Mockery::mock(SlackClient::class);

$slackMock->shouldReceive('send')->once();

app()->instance(SlackClient::class,$slackMock);

$notifier=app(Notifier::class);

$notifier->notifyAdmins('Testmessage');

}

There’salotmoreyoucandowithMockery:youcanusespies,andpartialspies,andmuchmore.GoingdeeperintohowtouseMockeryisoutofthescopeofthisbook,butIencourageyoutolearnmoreaboutthelibraryandhowitworks.

Page 472: Laravel: Up and Running: A Framework for Building Modern PHP

MockingFacadesLet’ssayyouhaveacontrollermethodthatcallsafacade.Now,youwanttotestthatcontrollermethod,andassertthatthatfacadecallshouldbemade.Howdoyoudoit?Thankfully,it’ssimple:treatthefacadelikeaninstanceofMockeryinyourtest.Example12-9showshowthisworks.

Example12-9.Mockingafacade//PeopleController

publicfunctionindex()

{

returnCache::remember('people',function(){

returnPerson::all();

});

}

//PeopleTest

publicfunctiontest_all_people_route_should_be_cached()

{

$person=factory(Person::class)->make();

Cache::shouldReceive('remember')

->once()

->andReturn(collect([$person]));

$this->visit('people')->seeJson(['name'=>$person->name]);

}

Asyoucansee,youcanusemethodslikeshouldReceive()onthefacades,justlikeyoudoonaMockeryobject.

AsofLaravel5.3,youcanalsouseyourfacadesasspies,whichmeansyoucansetyourassertionsattheendanduseshouldHaveReceived()insteadofshouldReceive().Example12-10illustratesthis.

Example12-10.Facadespiespublicfunctiontest_queue_job_should_be_pushed_after_regisration()

{

Cache::spy();

$this->post('register',['email'=>'[email protected]']);

Cache::shouldHaveReceived('push')

->with(SendWelcomeEmail::class,['email'=>'[email protected]']);

}

Page 473: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRLaravelcanworkwithanymodernPHPtestingframework,butitbringsinalotofframework-specificpowerifyouusePHPUnitandifyourtestsextendLaravel’sTestCase.Laravel’sapplicationtestingframeworkmakesitsimpletosendfakerequeststhroughyourapplicationandinspecttheresults,even“typing”ininputsand“clicking”buttonsbeforesubmittingaform.

TheTestCaseclassbringsinagroupofmethodsthatmakeiteasytocustomizehowyourtestsinteractwithyourdatabase,disabletheeffectsofevents,andmakeassertionsagainstframework-levelstructureslikejobsandfacades.

LaravelbringsinMockeryincaseyouneedmocks,stubs,spies,dummies,oranythingelse,butthetestingphilosophyofLaravelistouserealcollaboratorsasmuchaspossible.Don’tfakeitunlessyouhaveto.

Page 474: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter13.WritingAPIs

OneofthemostcommontasksLaraveldevelopersaregivenistocreateanAPI,usuallyJSONandRESTorREST-like,thatallowsthirdpartiestointeractwiththeLaravelapplication’sdata.

LaravelmakesitincrediblyeasytoworkwithJSON,anditsresourcecontrollersarealreadystructuredaroundRESTverbsandpatterns.Inthischapterwe’lllearnaboutsomebasicAPI-writingconcepts,thetoolsLaravelprovidesforwritingAPIs,andsomeexternaltoolsandorganizationalsystemsyou’llwanttoconsiderwhenwritingyourfirstLaravelAPI.

Page 475: Laravel: Up and Running: A Framework for Building Modern PHP

TheBasicsofREST-LikeJSONAPIsRepresentationalStateTransfer(REST)isanarchitecturalstyleforbuildingAPIs.Technically,RESTisabroaddefinitionthatcouldapplytoalmosttheentiretyoftheInternet,sodon’tletyourselfgetoverwhelmedbythedefinitionorcaughtinanargumentwithapedant.WhenwetalkaboutRESTfulorREST-likeAPIsintheLaravelworld,we’regenerallytalkingaboutAPIswithafewcommoncharacteristics:

Structuredaround“resources”thatcanbeuniquelyrepresentedbyURIs,like/catsforallcats,/cats/15forasinglecatwiththeIDof15,etc.

InteractionswithresourcesprimarilytakeplaceusingHTTPverbs(GET/cats/15versusDELETE/cats/15)

Stateless,meaningthere’snopersistentsessionauthenticationbetweenrequests;eachrequestmustuniquelyauthenticateitself

Cacheableandconsistent,meaningeachrequest(exceptforafewauthenticated-user-specificrequests)shouldreturnthesameresultregardlessofwhotherequesteris

ReturnJSON

ThemostcommonAPIpatternistohaveauniqueURLstructureforeachofyourEloquentmodelsthat’sexposedasanAPIresource,andallowforuserstointeractwiththatresourcewithspecificverbsandgetJSONback.Example13-1showsafewpossibleexamples.

Example13-1.CommonRESTAPIendpointstructuresGET/api/cats

[

{

id:1,

name:'Fluffy'

},

{

id:2,

name:'Killer'

}

]

GET/api/cats/2

{

id:2,

name:'Killer'

}

DELETE/api/cats/2

deletescat

POST/api/catswithbody:

{

name:'MrBigglesworth'

}

(createsnewcat)

PATCH/api/cats/3withbody:

{

name:'Mr.Bigglesworth'

Page 476: Laravel: Up and Running: A Framework for Building Modern PHP

}

(updatescat)

YoucanseethebasicsetofinteractionswearelikelytohavewithourAPIs.Let’sdigintohowtomakethemhappenwithLaravel.

Page 477: Laravel: Up and Running: A Framework for Building Modern PHP

ControllerOrganizationandJSONReturnsLaravel’sresourcecontrollersarestructuredverysimilarlytoaRESTfulAPIcontroller,solet’sgetstartedthere.Firstwe’llcreateanewcontrollerforourresource,whichwe’llrouteat/api/dogs:

phpartisanmake:controllerApi/\DogsController--resource

Remember,the--resourceflaggeneratesaresourcecontrollerinsteadofaplaincontroller.

Page 478: Laravel: Up and Running: A Framework for Building Modern PHP
Page 479: Laravel: Up and Running: A Framework for Building Modern PHP

ESCAPINGSLASHESINARTISANCOMMANDSNotethatinordertoputtheDogsControllerintheApinamespace,wehadtoescapethe\namespacebackslashwithaforwardslash.

Example13-2showswhatthatcontrollerwilllooklike.

Example13-2.Ageneratedresourcecontroller<?php

namespaceApp\Http\Controllers\Api;

useIlluminate\Http\Request;

useApp\Http\Requests;

useApp\Http\Controllers\Controller;

classDogsControllerextendsController

{

/**

*Displayalistingoftheresource.

*

*@return\Illuminate\Http\Response

*/

publicfunctionindex(){}

/**

*Showtheformforcreatinganewresource.

*

*@return\Illuminate\Http\Response

*/

publicfunctioncreate(){}

/**

*Storeanewlycreatedresourceinstorage.

*

*@param\Illuminate\Http\Request$request

*@return\Illuminate\Http\Response

*/

publicfunctionstore(Request$request){}

/**

*Displaythespecifiedresource.

*

*@paramint$id

*@return\Illuminate\Http\Response

*/

publicfunctionshow($id){}

/**

*Showtheformforeditingthespecifiedresource.

*

*@paramint$id

*@return\Illuminate\Http\Response

*/

publicfunctionedit($id){}

/**

*Updatethespecifiedresourceinstorage.

*

*@param\Illuminate\Http\Request$request

*@paramint$id

*@return\Illuminate\Http\Response

*/

publicfunctionupdate(Request$request,$id){}

/**

*Removethespecifiedresourcefromstorage.

Page 480: Laravel: Up and Running: A Framework for Building Modern PHP

*

*@paramint$id

*@return\Illuminate\Http\Response

*/

publicfunctiondestroy($id){}

}

Thedocblocksprettymuchtellthestory.index()listsallofthedogs,show()listsasingledog,create()showsthecreateview,store()storesadog,edit()showstheeditview,update()updatesadog,anddestroy()removesadog.

SincethisisanAPI,wecandeletecreate()andedit()offthebat;we’renotshowingviewshere.

Let’squicklymakeamodelandamigrationsowecanworkwithit:

phpartisanmake:modelDog--migration

phpartisanmigrate

Great!Nowwecanfilloutourcontrollermethods.

WecantakeadvantageofagreatfeatureofEloquenthere:ifyouechoanEloquentresultscollection,it’llautomaticallyconvertitselftoJSON(usingthe__toString()magicmethod,ifyou’recurious).Thatmeansifyoureturnacollectionofresultsfromaroute,you’llineffectbereturningJSON.

So,asExample13-3demonstrates,thiswillbesomeofthesimplestcodeyou’lleverwrite.

Example13-3.AsampleresourcecontrollerfortheDogentity...

classDogsControllerextendsController

{

publicfunctionindex()

{

returnDog::all();

}

publicfunctionstore(Request$request)

{

Dog::create($request->all());

}

publicfunctionshow($id)

{

returnDog::findOrFail($id);

}

publicfunctionupdate(Request$request,$id)

{

$dog=Dog::findOrFail($id);

$dog->update($request->all());

}

publicfunctiondestroy($id)

{

$dog=Dog::findOrFail($id);

$dog->delete();

}

}

Example13-4showshowwecanlinkthisupinourroutesfile.

Page 481: Laravel: Up and Running: A Framework for Building Modern PHP

Example13-4.Bindingtheroutesforaresourcecontroller//Routes.php

Route::group(['prefix'=>'api','namespace'=>'Api',function(){

Route::resource('dogs','DogsController');

}]);

Thereyouhaveit!YourfirstRESTfulAPIinLaravel.

Ofcourse,we’llneedmuchmorenuance:pagination,sorting,authentication,moredefinedresponseheaders.Butthisisthefoundationofeverythingelse.

Page 482: Laravel: Up and Running: A Framework for Building Modern PHP

ReadingandSendingHeadersRESTAPIsoftenread,andsend,non-contentinformationusingheaders.Forexample,anyrequesttoGitHub’sAPIwillreturnheadersdetailingthecurrentuser ’sratelimitingstatus:

X-RateLimit-Limit:5000

X-RateLimit-Remaining:4987

X-RateLimit-Reset:1350085394

Page 483: Laravel: Up and Running: A Framework for Building Modern PHP

X-*HEADERSYoumightbewonderingwhytheGitHubratelimitingheadersareprefixedwithX-,especiallyifyouseetheminthecontextofotherheadersreturnedwiththesamerequest:

HTTP/1.1200OK

Server:nginx

Date:Fri,12Oct201223:33:14GMT

Content-Type:application/json;charset=utf-8

Connection:keep-alive

Status:200OK

ETag:"a00049ba79152d03380c34652f2cb612"

X-GitHub-Media-Type:github.v3

X-RateLimit-Limit:5000

X-RateLimit-Remaining:4987

X-RateLimit-Reset:1350085394

Content-Length:5

Cache-Control:max-age=0,private,must-revalidate

X-Content-Type-Options:nosniff

AnyheaderwhosenamestartswithX-isaheaderthat’snotintheHTTPspec.Itmightbeentirelymadeup(e.g.,X-How-Much-Matt-Loves-This-Page),orpartofacommonconventionthathasn’tmadeitintothespecyet(e.g.,X-Requested-With).

Similarly,manyAPIsallowdeveloperstocustomizetheirrequestsusingrequestheaders.Forexample,GitHub’sAPImakesiteasytodefinewhichversionoftheAPIyou’dliketousewiththeAcceptheader:

Accept:application/vnd.github.v3+json

Ifyouweretochangev3tov2,GitHubwouldpassyourrequesttoversion2ofitsAPIinstead.

Let’slearnquicklyhowtodobothinLaravel.

Page 484: Laravel: Up and Running: A Framework for Building Modern PHP

SendingResponseHeadersinLaravelWealreadycoveredthistopicquiteabitinChapter10,buthere’saquickrefresher.Onceyouhavearesponseobject,youcanaddaheaderusingheader($headerName,$headerValue),asseeninExample13-5.

Example13-5.AddingaresponseheaderinLaravelRoute::get('dogs',function(){

returnresponse(Dog::all())

->header('X-Greatness-Index',9);

});

Niceandeasy.

Page 485: Laravel: Up and Running: A Framework for Building Modern PHP

ReadingRequestHeadersinLaravelIfwehaveanincomingrequest,it’salsosimpletoreadanygivenheader.Example13-6illustratesthis.

Example13-6.ReadingarequestheaderinLaravelRoute::get('dogs',function(Request$request){

echo$request->header('Accept');

});

NowthatyoucanreadincomingrequestheadersandsetheadersonyourAPIresponses,let’stakealookathowyoumightwanttocustomizeyourAPI.

Page 486: Laravel: Up and Running: A Framework for Building Modern PHP

EloquentPaginationPaginationisoneofthefirstplaceswheremostAPIsneedtoconsiderspecialinstructions.Eloquentcomesoutoftheboxwithapaginationsystemthathooksdirectlyintothequeryparametersofanypagerequest.WealreadycoveredthepaginatorcomponentabitinChapter5,buthere’saquickrefresher.

AnyEloquentcallprovidesapaginate()method,whichyoucanpassthenumberofitemsyou’dliketoreturnperpage.EloquentthencheckstheURLforapagequeryparameterand,ifit’sset,treatsthatasanindicatorofhowmanypagestheuserisintoapaginatedlist.

TomakeyourAPIroutereadyforautomatedLaravelpagination,usepaginate()insteadofall()orget()inyourroute;somethinglikeExample13-7.

Example13-7.ApaginatedAPIrouteRoute::get('dogs',function(){

returnDog::paginate(20);

});

We’vedefinedthatEloquentshouldget20resultsfromthedatabase.Dependingonwhatthepagequeryparameterissetto,Laravelwillknowexactlywhich20resultstopullforus:

GET/dogs-Returnresults1-20

GET/dogs?page=1-Returnresults1-20

GET/dogs?page=2-Returnresults21-40

Notethatthepaginate()methodisalsoavailableonquerybuildercalls,asseeninExample13-8.

Example13-8.Usingthepaginate()methodonaquerybuildercallRoute::get('dogs',function(){

returnDB::table('dogs')->paginate(20);

});

Here’ssomethinginteresting,though:thisisn’tjustgoingtoreturn20resultswhenyouconvertittoJSON.Instead,it’sgoingtobuildaresponseobjectthatautomaticallypassessomeusefulpagination-relateddetailstotheenduser,alongwiththepaginateddata.Example13-9showsapossibleresponsefromourcall,truncatedtoonlythreerecordstosavespace.

Example13-9.Sampleoutputfromapaginateddatabasecall{

"total":50,

"per_page":3,

"current_page":1,

"last_page":17,

"next_page_url":"http://myapp.com/api/dogs?page=2",

"prev_page_url":null,

"from":1,

"to":3,

"data":[

{

'name':'Fido'

Page 487: Laravel: Up and Running: A Framework for Building Modern PHP

},

{

'name':'Pickles'

},

{

'name':'Spot'

}

]

}

Page 488: Laravel: Up and Running: A Framework for Building Modern PHP

SortingandFilteringWhilethereisaconventionandsomebuilt-intoolingforpaginationinLaravel,thereisn’tanyforsorting,soyouhavetofigurethatoutonyourown.I’llgiveaquickcodesamplehere,andI’llstyleourqueryparameterssimilarlytotheJSONAPIspec(describedinthefollowingsidebar).

THEJSONAPISPEC

TheJSONAPIisastandardforhowtohandlemanyofthemostcommontasksinbuildingJSON-basedAPIs:filtering,sorting,pagination,authentication,embedding,links,metadata,andmore.

Laravel’sdefaultpaginationdoesn’tworkexactlyaccordingtotheJSONAPIspec,butitgetsyoustartedintherightdirection.AndthemajorityoftherestoftheJSONAPIspecissomethingyou’lljusthavetochoose(ornot)toimplementmanually.

Forexample,here’sapieceoftheJSONAPIspecthathelpfullyhandleshowtostructuredataversuserrorreturns:

AdocumentMUSTcontainatleastoneofthefollowingtop-levelmembers:

data:thedocument’s“primarydata”

errors:anarrayoferrorobjects

meta:ametaobjectthatcontainsnon-standardmeta-information.

ThemembersdataanderrorsMUSTNOTcoexistinthesamedocument.

Bewarned,however:it’swonderfultohavetheJSONAPIasaspec,butitalsotakesquiteabitofgroundworktogetrunningwithit.Wewon’tuseitentirelyintheseexamples,butI’lluseitsgeneralideasasinspiration.

Page 489: Laravel: Up and Running: A Framework for Building Modern PHP

SortingYourAPIResultsFirst,let’ssetuptheabilitytosortourresults.WestartinExample13-10withtheabilitytosortbyonlyasinglecolumn,andinonlyasingledirection.

Example13-10.SimplestAPIsorting//Handles/dogs?sort=name

Route::get('dogs',function(Request$request){

//Getthesortqueryparameter(orfallbacktodefaultsort"name")

$sortCol=$request->input('sort','name');

returnDog::orderBy($sortCol)->paginate(20);

});

Weaddtheabilitytoinvertit(e.g.,?sort=-weight)inExample13-11.

Example13-11.Single-columnAPIsorting,withdirectioncontrol//Handles/dogs?sort=nameand/dogs?sort=-name

Route::get('dogs',function(Request$request){

//Getthesortqueryparameter(orfallbacktodefaultsort"name")

$sortCol=$request->input('sort','name');

//Setthesortdirectionbasedonwhetherthekeystartswith-

//usingLaravel'sstarts_with()helperfunction

$sortDir=starts_with($sortCol,'-')?'desc':'asc';

$sortCol=ltrim($sort,'-');

returnDog::orderBy($sortCol,$sortDir)

->paginate(20);

});

Finally,wedothesameformultiplecolumns(e.g.,?sort=name,-weight)inExample13-12.

Example13-12.JSONAPI–stylesorting//Handles?sort=name,-weight

Route::get('dogs',function(Request$request){

//Grabthequeryparameterandturnitintoanarrayexplodedby,

$sorts=explode(',',$request->input('sort',''));

//Createaquery

$query=Dog::query();

//Addthesortsonebyone

foreach($sortsas$sortCol){

$sortDir=starts_with($sortCol,'-')?'desc':'asc';

$sortCol=ltrim($sort,'-');

$query->orderBy($sortCol,$sortDir);

}

//Return

return$query->paginate(20);

});

Asyoucansee,it’snotthesimplestprocessever,andyou’lllikelywanttobuildsomehelpertoolingaroundtherepetitiveprocesses,butwe’rebuildingupthecustomizabilityofourAPIpiecebypieceusinglogicalandsimplefeatures.

Page 490: Laravel: Up and Running: A Framework for Building Modern PHP

FilteringYourAPIResultsAnothercommontaskinbuildingAPIsisfilteringoutallbutacertainsubsetofdata.Forexample,theclientmightaskforalistofthedogsthatarefemale.

TheJSONAPIdoesn’tgiveusanygreatideasforsyntaxhere,otherthanthatweshouldusethefilterqueryparameter.Let’sthinkalongthelinesofthesortsyntax,wherewe’reputtingeverythingintoasinglekey—maybe?filter=sex:female.YoucanseehowtodothisinExample13-13.

Example13-13.SinglefilteronAPIresultsRoute::get('dogs',function(Request$request){

$query=Dog::query();

if($request->has('filter')){

list($criteria,$value)=explode(':',$request->input('filter'));

$query->where($criteria,$value);

}

return$query->paginate(20);

});

And,justforkicks,inExample13-14weallowformultiplefilters,like?filter=sex:female,color:brown.

Example13-14.MultiplefiltersonAPIresultsRoute::get('dogs',function(Request$request){

$query=Dog::query();

if($request->has('filter')){

$filters=explode(',',$request->input('filter'));

foreach($filtersas$filter){

list($criteria,$value)=explode(':',$filter);

$query->where($criteria,$value);

}

}

return$query->paginate(20);

});

Page 491: Laravel: Up and Running: A Framework for Building Modern PHP

TransformingResultsWe’vecoveredhowtosortandfilterourresultsets.Butrightnow,we’rerelyingonEloquent’sJSONserialization,whichmeanswegeteveryfieldoneverymodel.

Eloquentprovidesafewconveniencetoolsfordefiningwhichfieldstoshowwhenyou’reserializinganarray.YoucanreadmoreinChapter8,butthegististhatifyouseta$hiddenarraypropertyonyourEloquentclass,anyfieldlistedinthatarraywillnotbeshownintheserializedmodeloutput.Youcanalternativelyseta$visiblearraythatdefinesthefieldsthatareallowedtobeshown.YoucouldalsoeitheroverwriteormimicthetoArray()functiononyourmodel,craftingacustomoutputformat.

Anothercommonpatternistocreateatransformerforeachdatatype.There’safantasticpackageforthis,Fractal,thatsetsupaseriesofconveniencestructuresandclassesfortransformingyourdata,butlet’scoverasimpleimplementationtoshowwhatatransformerisandwhyyoumightwanttodothis.

Page 492: Laravel: Up and Running: A Framework for Building Modern PHP

WritingYourOwnTransformerThegeneralconceptofatransformeristhatwearegoingtoruneveryinstanceofourmodelthroughanotherclassthattransformsitsdatatoadifferentstate.Itmightaddfields,renamefields,deletefields,manipulatefields,addnestedchildren,orwhateverelse.Let’sstartwithasimpleexample(Example13-15).

Example13-15.AsimpletransformerRoute::get('users/{id}',function($userId){

return(newUserTransformer(User::findOrFail($userId)));

});

classUserTransformer

{

protected$user;

publicfunction__construct($user)

{

$this->user=$user;

}

publicfunctiontoArray()

{

return[

'id'=>$this->user->id,

'name'=>sprintf(

"%s%s",

$this->user->first_name,

$this->user->last_name

),

'friendsCount'=>$this->user->friends->count()

];

}

publicfunctiontoJson()

{

returnjson_encode($this->toArray());

}

publicfunction__toString()

{

return$this->toJson();

}

}

Page 493: Laravel: Up and Running: A Framework for Building Modern PHP
Page 494: Laravel: Up and Running: A Framework for Building Modern PHP

CLASSICTRANSFORMERSAmoreclassictransformerwouldprobablyofferatransform()methodthattakesa$userparameter.ThiswouldlikelyspitoutanarrayorJSONdirectly.

However,I’vebeenusingthispattern,whichwesometimescall“APIobjects,”forafewyearsandreallylovehowmuchmorepowerandflexibilityitprovides.

AsyoucanseeinExample13-15,transformersacceptthemodelthey’retransformingasaparameterandthenmanipulatethatmodel—anditsrelationships—tocreatethefinaloutputthatyouwanttosendtotheAPI.

Thisgivesyoumorecontrol,isolatesAPI-specificlogicawayfromthemodelitself,andallowsyoutoprovideamoreconsistentAPIevenwhenthemodelsandtheirrelationshipschangedowntheroad.

Page 495: Laravel: Up and Running: A Framework for Building Modern PHP

NestingandRelationshipsWhether,andhow,tonestrelationshipsinAPIsisanissueofmuchdebate.Thankfully,peoplemoreexperiencedthanmehavewrittenonthisatlength;I’drecommendreadingPhilSturgeon’sBuildAPIsYouWon’tHate(Leanpub)tolearnmoreaboutthisandaboutRESTAPIsingeneral.

Thereareafewprimarywaystoapproachnestingrelationships.Theseexampleswillassumeyourprimaryresourceisauserandyourrelatedresourceisafriend:

Embedrelatedresourcesdirectlyintheprimaryresource(e.g.,theusers/5resourcehasitsfriendsnestedinit).

Embedjusttheforeignkeysintheprimaryresource(e.g.,theusers/5resourcehasanarrayoffriendIDsnestedinit).

Allowtheusertoquerytherelatedresourcefilteredbytheoriginatingresource(e.g.,/friends?user=5,or“givemeallfriendswhoarerelatedtouser#5”).

Createasubresource(e.g.,/users/5/friends).

Allowoptionalembedding(e.g.,/users/5doesnotembed,but/users/5?embed=friendsdoesembed;sodoes/users/5?embed=friends,dogs).

Let’sassumeforaminutethatwewantto(optionally)embedtherelationships.Howwouldwedothat?OurtransformerexampleinExample13-15givesusagreatheadstart.Let’sadjustitinExample13-16toaddoptionalembedding.

Example13-16.AllowingforoptionalembeddingofaresourceinatransformerRoute::get('users/{id}',function($userId,Request$request){

//Gettheembedsqueryparameterandsplitbycommas

$embeds=explode(',',$request->input('embed',''));

//Passbothuserandembedstotheusertransformer

return(newUserTransformer(User::findOrFail($userId),$embeds));

});

classUserTransformer

{

protected$user;

protected$embeds;

publicfunction__construct($user,$embeds=[])

{

$this->user=$user;

$this->embeds=$embeds;

}

publicfunctiontoArray()

{

$append=[];

if(in_array('friends',$this->embeds)){

//Ifyouhavemorethanoneembed,you'llwanttogeneralizethis

$append['friends']=$this->user->friends->map(function($friend){

return(newFriendTransformer($friend))->toArray();

});

}

Page 496: Laravel: Up and Running: A Framework for Building Modern PHP

returnarray_merge([

'id'=>$this->user->id,

'name'=>sprintf(

"%s%s",

$this->user->first_name,

$this->user->last_name

)

],$append);

}

...

We’lllearnmoreaboutthemap()functionalitywhenwelookatcollectionsinChapter17,buteverythingelseinhereshouldbeprettyfamiliar.

Intheroute,we’resplittingtheembedqueryparameterbycommasandpassingitintoourtransformer.Currentlyourtransformercanjusthandlethefriendsembed,butitcouldbeabstractedtohandleothers.Iftheuserhasrequestedthefriendsembed,thetransformermapsovereachfriend(usingthehasmanyfriendsrelationshipontheusermodel),passesthatfriendtotheFriendTransformer,andembedsthearrayofalltransformedfriendsintheuserresponse.

Page 497: Laravel: Up and Running: A Framework for Building Modern PHP

APIAuthenticationwithLaravelPassportMostAPIsrequiresomeformofauthenticationtoaccesssomeorallofthedata.Laravel5.2introducedasimple“token”authenticationscheme,whichwe’llcovershortly,butinLaravel5.3wegotanewtoolcalledPassport(bywayofaseparatepackage,broughtinviaComposer)thatmakesiteasytosetupafull-featuredOAuth2.0serverinyourapplication,completewithanAPIandUIcomponentsformanagingclientsandtokens.PassportandthefeaturesitreliesonareonlycompatiblewithLaravel5.3andabove.

Page 498: Laravel: Up and Running: A Framework for Building Modern PHP

ABriefIntroductiontoOAuth2.0OAuthisbyfarthemostcommonauthsystemusedinRESTfulAPIs.Unfortunately,it’sfartoocomplexatopicforustocoverhereindepth.Forfurtherreading,MattFrosthaswrittenagreatbookonOAuthandPHPtitledIntegratingWebServiceswithOAuthandPHP(php[architect]).

Ifyou’reworkingwithLaravel5.1or5.2,there’saLaravelpackagecalledOAuth2.0ServerforLaravelthatmakesitrelativelyeasytoaddabasicOAuth2.0authenticationservertoyourLaravelapplication.It’saLaravelconveniencebridgetoaPHPpackagecalledPHPOAuth2.0Server.

However,ifyou’reonLaravel5.3,Passportgivesyoueverythingprovidedbythatpackageandmuchmore,withasimplerandmorepowerfulAPIandinterface.

Page 499: Laravel: Up and Running: A Framework for Building Modern PHP

InstallingPassportPassportisaseparatepackage,soyourfirststepistoinstallit.I’llsumupthestepshere,butyoucangetmorein-depthinstallationinstructionsinthedocs.

First,bringitinwithComposer:

composerrequirelaravel/passport

Next,addLaravel\Passport\PassportServiceProvider::classtotheprovidersarrayofconfig/app.php.ThiswillmakePassportbootupeverytimeyourapploads.

Passportimportsaseriesofmigrations,sorunthosewithphpartisanmigratetocreatethetablesnecessaryforOAuthclients,scopes,andtokens.

Next,runtheinstallerwithphpartisanpassport:install.ThisisgoingtocreateencryptionkeysfortheOAuthserver(storage/oauth-private.keyandstorage/oauth-public.key)andinsertOAuthclientsintothedatabaseforourpersonalandpasswordgranttypetokens(whichwe’llcoverlater).

You’llneedtoimporttheLaravel\Passport\HasApiTokenstraitintoyourUsermodel;thiswilladdOAuthclient-andtoken-relatedrelationshipstoeachUser,aswellasafewtoken-relatedhelpermethods.Next,addacalltoLaravel\Passport\Passport::routes()intheboot()methodoftheAuthServiceProvider.Thiswilladdthefollowingroutes:

oauth/authorize

oauth/clients

oauth/clients/{client_id}

oauth/personal-access-tokens

oauth/personal-access-tokens/{token_id}

oauth/scopes

oauth/token

oauth/token/refresh

oauth/tokens

oauth/tokens/{token_id}

Finally,lookfortheapiguardinconfig/auth.php.Bydefaultthisguardwillusethetokendriver(whichwe’llcovershortly),butwe’llchangethattobethepassportdriverinstead.

Page 500: Laravel: Up and Running: A Framework for Building Modern PHP

YounowhaveafullyfunctionalOAuth2.0server!Youcancreatenewclientswithphpartisanpassport:client,andyouhaveanAPIformanagingyourclientsandtokensavailableunderthe/oauthrouteprefix.

ToprotectaroutebehindyourPassportauthsystem,addtheauth:apimiddlewaretotherouteorroutegroup,asshowninExample13-17.

Example13-17.ProtectinganAPIroutewiththePassportauthmiddleware//routes/api.php

Route::get('/user',function(Request$request){

return$request->user();

})->middleware('auth:api');

Inordertoauthenticatetotheseprotectedroutes,yourclientappswillneedtopassatoken(we’llcoverhowtogetonenext)asaBearertokenintheAuthorizationheader.Example13-18showswhatthiswouldlooklikeifyouweremakingarequestusingtheGuzzleHTTPlibrary.

Example13-18.MakingasampleAPIrequestwithaBearertoken$http=newGuzzleHttp\Client;

$response=$http->request('GET','http://speakr.dev/api/user',[

'headers'=>[

'Accept'=>'application/json',

'Authorization'=>'Bearer'.$accessToken,

],

]);

Now,let’slearnalittlemoreabouthowitallworks.

Page 501: Laravel: Up and Running: A Framework for Building Modern PHP

Passport’sAPIPassportexposesanAPIinyourapplicationunderthe/oauthrouteprefix.TheAPIprovidestwoprimaryfunctions:first,toauthorizeuserswithOAuth2.0authorizationflows(/oauth/authorizeand/oauth/token),andsecond,toallowuserstomanagetheirclientsandtokens(therestoftheroutes).

Thisisanimportantdistinction,especiallyifyou’reunfamiliarwithOAuth.EveryOAuthserverneedstoexposetheabilityforconsumerstoauthenticatewithyourserver;that’stheentirepointoftheservice.ButPassportalsoexposesanAPIformanagingthestateofyourOAuthserver ’sclientsandtokens.ThismeansyoucaneasilybuildafrontendtoletyourusersmanagetheirinformationinyourOAuthapplication,andPassportactuallycomeswithVue-basedmanagercomponentsthatyoucaneitheruseoruseforinspiration.

We’llcovertheAPIroutesthatallowyoutomanageclientsandtokens,andtheVuecomponentsthatPassportshipswithtomakeiteasy,butfirstlet’sdigintothevariouswaysyouruserscanauthenticatewithyourPassport-protectedAPI.

Page 502: Laravel: Up and Running: A Framework for Building Modern PHP

Passport’sAvailableGrantTypesPassportmakesitpossibleforyoutoauthenticateusersinfourdifferentways.TwoaretraditionalOAuth2.0grants(thepasswordgrantandauthorizationcodegrant)andtwoareconveniencemethodsthatareuniquetoPassport(thepersonaltokenandsynchronizertoken).

PasswordgrantThepasswordgrant,whilelesscommonthantheauthorizationcodegrant,ismuchsimpler.IfyouwantuserstobeabletoauthenticatedirectlywithyourAPIusingtheirusernameandpassword—forexample,ifyouhaveamobileappforyourcompanyconsumingyourownAPI—youcanusethepasswordgrant.

Page 503: Laravel: Up and Running: A Framework for Building Modern PHP
Page 504: Laravel: Up and Running: A Framework for Building Modern PHP

CREATINGAPASSWORDGRANTCLIENTInordertousethepasswordgrantflow,youneedapasswordgrantclientinyourdatabase.Onewillhavebeenaddedwhenyouranphpartisanpassport:install,butifyoueverneedtogenerateanewpasswordgrantclientforanyreason,youcan:

phpartisanpassport:client--password

Whatshouldwenamethepasswordgrantclient?

[MyApplicationPasswordGrantClient]:

>SpaceBook_internal

Passwordgrantclientcreatedsuccessfully.

Withthepasswordgranttype,thereisjustonesteptogettingatoken:sendingtheuser ’scredentialstothe/oauth/tokenroute,likeinExample13-19.

Example13-19.Makingarequestwiththepasswordgranttype//AssumingSpaceBookisnotanexternalapp,butactually

//atrustedinternalapp...thisisSpaceBook'sroutes/web.php

Route::get('speakr/password-grant-auth',function(){

$http=newGuzzleHttp\Client;

$response=$http->post('http://speakr.dev/oauth/token',[

'form_params'=>[

'grant_type'=>'password',

'client_id'=>config('speakr.id'),

'client_secret'=>config('speakr.secret'),

'username'=>'[email protected]',

'password'=>'my-speakr-password',

],

]);

$thisUsersTokens=json_decode((string)$response->getBody(),true);

//dostuffwiththetokens

});

Thisroutewillreturnanaccess_tokenandarefresh_token.YoucannowsavethosetokenstousetoauthenticatewiththeAPI(accesstoken)andtorequestmoretokenslater(refreshtoken).

NotethattheIDandsecretwe’duseforthepasswordgranttypewouldbethoseintheclientsdatabasetableofourPassportappintherowwiththenameSpaceBook_internal.

AuthorizationcodegrantThemostcommonOAuth2.0authworkflowisalsothemostcomplexonePassportsupports.Let’simaginewe’redevelopinganapplicationthat’slikeTwitterbutforsoundclips;we’llcallitSpeakr.Andwe’llimagineanotherwebsite,asocialnetworkforsciencefictionfans,calledSpaceBook.SpaceBook’sdeveloperwantstoletpeopleembedtheirSpeakrdataintotheirSpaceBooknewsfeeds.We’regoingtoinstallPassportinourappsothatotherapps—SpaceBook,forexample—canallowtheirmutualuserstoauthenticatewiththeirSpeakrinformation.

Page 505: Laravel: Up and Running: A Framework for Building Modern PHP

Intheauthorizationcodegranttype,eachconsumingwebsite—SpaceBook,inthisexample—needstocreatea“client”inourPassport-enabledapp.Inmostscenarios,theothersites’adminswillhaveuseraccountsatSpeakr,andwe’llbuildtoolsforthemtocreateclientsthere.Butforstarters,wecanjustmanuallycreateaclientfortheSpaceBookadmins:

phpartisanpassport:client

WhichuserIDshouldtheclientbeassignedto?:

>1

Whatshouldwenametheclient?:

>SpaceBook

Whereshouldweredirecttherequestafterauthorization?

[http://passport.dev/auth/callback]:

>http://spacebook.dev/auth/callback

Newclientcreatedsuccessfully.

ClientID:3

Clientsecret:RiQstsWDqd9SqQY3lQhiZF50ulKdw4iPhPAdkeO3

Everyclientneedstobeassignedtoauserinyourapp.ImagineJill,user#1,iswritingSpaceBook;she’llbethe“owner”ofthisclientwe’recreating.

NowwehavetheIDandsecretfortheSpaceBookclient.Atthispoint,SpaceBookcanusethisIDandsecrettobuildtoolingallowinganindividualSpaceBookuser(whoisalsoaSpeakruser)togetanauthtokenfromSpeakrforusewhenSpaceBookwantstomakeAPIcallstoSpeakronthatuser ’sbehalf.Example13-20illustratesthis.(ThisandthefollowingexamplesassumeSpaceBookisaLaravelapp,too;theyalsoassumewe’vecreatedafileatconfig/speakr.phpthatreturnstheIDandsecretwejustcreated.)

Example13-20.AconsumerappredirectingausertoourOAuthserver//InSpaceBook'sroutes/web.php:

Route::get('speakr/redirect',function(){

$query=http_build_query([

'client_id'=>config('speakr.id'),

'redirect_uri'=>url('speakr/callback'),

'response_type'=>'code',

]);

//Buildsastringlike:

//client_id={$client_id}&redirect_uri={$redirect_uri}&response_type=code

returnredirect('http://speakr.dev/oauth/authorize?'.$query);

});

WhenusershitthatrouteinSpaceBook,they’llnowberedirectedtothe/oauth/authorizePassportrouteonourSpeakrapp.Atthispointthey’llseeaconfirmationpage;youcanusethedefaultPassportconfirmationpagebyrunningthiscommand:

phpartisanvendor:publish--tag=passport-views

Thiswillpublishtheviewtoresources/views/vendor/passport/authorize.blade.php,andyouruserswillseethepageshowninFigure13-1.

Page 506: Laravel: Up and Running: A Framework for Building Modern PHP

Figure13-1.OAuthauthorizationcodeapprovalpage

Onceauserchoosestoacceptorrejecttheauthorization,Passportwillredirectthatuserbacktotheprovidedredirect_uri.InExample13-20wesetaredirect_uriofurl('speakr/callback'),sotheuserwillberedirectedbacktohttp://spacebook.dev/speakr/callback.

Anapprovalrequestwillcontainacodethatourconsumerapp’scallbackroutecannowusetogetatokenbackfromourPassport-enabledapp,Speakr.Arejectionrequestwillcontainanerror.SpaceBook’scallbackroutemightlooksomethinglikeExample13-21.

Example13-21.Theauthorizationcallbackrouteinoursampleconsumingapp//InSpaceBook'sroutes/web.php:

Route::get('speakr/callback',function(Request$request){

if($request->has('error')){

//handleerrorcondition

}

$http=newGuzzleHttp\Client;

$response=$http->post('http://speakr.dev/oauth/token',[

'form_params'=>[

'grant_type'=>'authorization_code',

'client_id'=>config('speakr.id'),

'client_secret'=>config('speakr.secret'),

'redirect_uri'=>url('speakr/callback'),

'code'=>$request->code,

],

]);

$thisUsersTokens=json_decode((string)$response->getBody(),true);

//dostuffwiththetokens

});

Whatwe’redoinghereisbuildingaGuzzleHTTPrequesttothe/oauth/tokenPassportrouteonSpeakr.WesendaPOSTrequestcontainingtheauthorizationcodewereceivedwhentheuserapprovedaccess,andSpeakrwillreturnaJSONresponsecontainingafewkeys:

access_tokenisthetokenSpaceBookwillwanttosaveforthisuser.ThistokeniswhattheuserwillusetoauthenticateinfuturerequeststoSpeakr.

refresh_tokenisatokenSpaceBookwillneedifyoudecidetosetyourtokenstoexpire.

Page 507: Laravel: Up and Running: A Framework for Building Modern PHP

Bydefault,Passport’saccesstokensneverneedtoberefreshed,soyoudon’tneedtoconcernyourselfwiththisandcanjustignoreit.

expires_inisthenumberofsecondsuntilanaccess_tokenexpires(needstoberefreshed).

Page 508: Laravel: Up and Running: A Framework for Building Modern PHP

USINGREFRESHTOKENS

Ifyou’dliketoforceuserstoreauthenticatemoreoften,youneedtosetashorterrefreshtimeonthetokens,andthenyoucanusetherefresh_tokentorequestanewaccess_token.

Tosetashorterrefreshtime:

//AuthServiceProvider'sboot()method

publicfunctionboot()

{

$this->registerPolicies();

Passport::routes();

//Howlongatokenlastsbeforeneedingrefreshing

Passport::tokensExpireIn(

Carbon::now()->addDays(15)

);

//Howlongarefreshtokenwilllastbeforere-auth

Passport::refreshTokensExpireIn(

Carbon::now()->addDays(30)

);

}

Torequestanewtokenusingarefreshtoken,youneedtohavefirstsavedtherefresh_tokenfromtheinitialauthresponseinExample13-21.Onceit’stimetorefresh,you’llmakeacallsimilartothatexample,butmodifiedslightly:

//InSpaceBook'sroutes/web.php:

Route::get(

'speakr/request-refresh',

function(Request$request){

$http=newGuzzleHttp\Client;

$params=[

'grant_type'=>'refresh_token',

'client_id'=>config('speakr.id'),

'client_secret'=>config('speakr.secret'),

'redirect_uri'=>url('speakr/callback'),

'refresh_token'=>$theTokenYouSavedEarlier,

];

$response=$http->post(

'http://speakr.dev/oauth/token',

['form_params'=>$params,]

);

$thisUsersTokens=json_decode(

(string)$response->getBody(),

true

);

//dostuffwiththetokens

}

);

Intheresponse,you’llreceiveafreshsetoftokenstosavetoyouruser.

Younowhaveallthetoolsyouneedtoperformbasicauthorizationcodeflows.We’llcoverhowtobuildanadminpanelforyourclientsandtokenslater,butfirst,let’stakeaquicklookattheothergranttypes.

Page 509: Laravel: Up and Running: A Framework for Building Modern PHP

PersonalaccesstokensTheauthorizationcodegrantisgreatforyourusers’appsandthepasswordcodegrantisgreatforyourownapps,butwhatifyouruserswanttocreatetokensforthemselvestotestoutyourAPIortousewhenthey’redevelopingtheirapps?That’swhatpersonaltokensarefor.

Page 510: Laravel: Up and Running: A Framework for Building Modern PHP

CREATINGAPERSONALACCESSCLIENTInordertocreatepersonaltokens,youneedapersonalaccessclientinyourdatabase.Runningphpartisanpassport:installwillhaveaddedonealready,butifyoueverneedtogenerateanewpersonalaccessclientforanyreason,youcanrunphpartisanpassport:client--personal:

phpartisanpassport:client--personal

Whatshouldwenamethepasswordgrantclient?

[MyApplicationPersonalAccessClient]:

>MyApplicationPersonalAccessClient

Personalaccessclientcreatedsuccessfully.

Personalaccesstokensarenotquitea“grant”type;there’snoOAuth-prescribedflowhere.Rather,they’reaconveniencemethodthatPassportaddstomakeiteasytohaveasingleclientregisteredinyoursystemthatexistssolelyfortheeasycreationofconveniencetokensforyouruserswhoaredevelopers.

Forexample,maybeyouhaveauserwho’sdevelopingacompetitortoSpaceBooknamedRaceBook(it’sformarathonrunners),andhewantstotoyaroundwiththeSpeakrAPIabittofigureouthowitworksbeforestartingtocode.Doesthisdeveloperhavethefacilitytocreatetokensusingtheauthorizationcodeflow?Notyet—hehasn’tevenwrittenanycodeyet!That’swhatpersonalaccesstokensarefor.

YoucancreatepersonalaccesstokensthroughtheJSONAPI,whichI’llcovershortly,butyoucanalsocreateoneforyouruserdirectlyincode:

//Creatingatokenwithoutscopes

$token=$user->createToken('TokenName')->accessToken;

//Creatingatokenwithscopes...

$token=$user->createToken('MyToken',['place-orders'])->accessToken;

Youruserscanusethesetokensjustasiftheyweretokenscreatedwiththeauthorizationcodegrantflow.

TokensfromLaravelsessionauthentication(synchronizertokens)There’sonefinalwayforyouruserstogettokenstoaccessyourAPI,andit’sanotherconveniencemethodthatPassportaddsbutwhichnormalOAuthserversdon’tprovide.Thismethodisforwhenyourusersarealreadyauthenticatedbecausethey’veloggedintoyourLaravelapplikenormal,andyouwanttheJavaScriptonyourLaravelapptobeabletoaccesstheAPI.It’dbeapaintohavetoreauthenticatetheuserswiththeauthorizationcodeorpasswordgrantflow,soLaravelprovidesahelperforthat.

IfyouaddtheLaravel\Passport\Http\Middleware\CreateFreshApiTokenmiddlewaretoyourwebmiddlewaregroup(inapp/Http/Kernel.php),everyresponseLaravelsendstoyour

Page 511: Laravel: Up and Running: A Framework for Building Modern PHP

authenticateduserswillhaveacookienamedlaravel_tokenattachedtoit.ThiscookieisaJSONWebToken(JWT)thatcontainsencodedinformationabouttheCSRFtoken.Now,ifyousendthenormalCSRFtokenwithyourJavaScriptandsenditalongintheX-CSRF-TOKENheaderonanyAPIrequestsyoumake,theAPIwillcompareyourCSRFtokenwiththiscookieandthiswillauthenticateyouruserstotheAPIjustlikeanyothertoken.

JSONWEBTOKENS( JWT)

JWTisanewerformatthatisjustbeginningtogainprominence.AJSONWebTokenisaJSONobjectcontainingalloftheinformationnecessarytodetermineauser’sauthenticationstateandaccesspermissions.ThisJSONobjectisdigitallysignedusingakeyed-hashmessageauthenticationcode(HMAC)orRSA,whichiswhatmakesittrustworthy.

ThetokenisusuallyencodedandthendeliveredviaURL,POSTrequest,orinaheader.Onceauserauthenticateswiththesystemsomehow,everyHTTPrequestafterthatwillcontainthetoken,describingtheuser’sidentityandauthorization.

JSONWebTokensconsistofthreeBase64-encodedstringsseparatedbydots(.);somethinglikexxx.yyy.zzz.ThefirstsectionisaBase64-encodedJSONobjectcontaininginformationaboutwhichhashingalgorithmisbeingused;thesecondsectionisaseriesof“claims”abouttheuser’sauthorizationandidentity;andthethirdisthesignature,orthefirstandsecondsectionsencryptedandsignedusingthealgorithmspecifiedinthefirstsection.

TolearnmoreaboutJWT,checkoutJWT.ioorthejwt-authLaravelpackage.

ThedefaultVuesetupthatLaravelcomesbundledwithsetsupthisheaderforyou,butifyou’reusingadifferentframework,you’llneedtosetitupmanually.Example13-22showshowtodoitwithjQuery.

Example13-22.SettingjQuerytopassLaravel’sCSRFtokenswithallAjaxrequests$.ajaxSetup({

headers:{

'X-CSRF-TOKEN':"{{csrf_token()}}"

}

});

IfyouaddtheCreateFreshApiTokensmiddlewaretoyourwebmiddlewaregroupandpassthatheaderwitheveryJavaScriptrequest,yourJavaScriptrequestswillbeabletohityourPassport-protectedAPIrouteswithoutworryingaboutanyofthecomplexityoftheauthorizationcodeorpasswordgrants.

Page 512: Laravel: Up and Running: A Framework for Building Modern PHP

ManagingClientsandTokenswiththePassportAPIandtheVueComponentsNowthatwe’vecoveredhowtomanuallycreateclientsandtokensandhowtoauthorizeasaconsumer,let’stakealookattheaspectsofthePassportAPIthatmakeitpossibletobuilduserinterfaceelementsallowingyouruserstomanagetheirclientsandtokens.

TheroutesTheeasiestwaytodigintotheAPIroutesisbylookingathowthesampleprovidedVuecomponentsworkandwhichroutestheyrelyon,soI’lljustgiveabriefoverview:

/oauth/clients(GET,POST)

/oauth/clients/{id}(DELETE,PUT)

/oauth/personal-access-tokens(GET,POST)

/oauth/personal-access-tokens/{id}(DELETE)

/oauth/scopes(GET)

/oauth/tokens(GET)

/oauth/tokens/{id}(DELETE)

Asyoucansee,wehaveafewentitieshere(clients,personalaccesstokens,scopes,andtokens).Wecanlistallofthem;wecancreatesome(youcan’tcreatescopes,sincethey’redefinedincode,andyoucan’tcreatetokens,becausethey’recreatedintheauthorizationflow);andwecandeleteandupdatesome.

TheVuecomponentsPassportcomeswithasetofVuecomponentsoutoftheboxthatmakeiteasytoallowyouruserstoadministertheirclients(thosethey’vecreated),authorizedclients(thosethey’veallowedaccesstotheiraccount),andpersonalaccesstokens(fortheirowntestingpurposes).

Topublishthesecomponentsintoyourapplication,runthiscommand:

phpartisanvendor:publish--tag=passport-components

You’llnowhavethreenewVuecomponentsinresources/assets/js/components/passport.ToaddthemtoyourVuebootstrapsothey’reaccessibleinyourtemplates,registertheminyourresources/assets/js/app.jsfileasshowninExample13-23.

Example13-23.ImportingPassport’sVuecomponentsintoapp.jsrequire('./bootstrap');

Vue.component(

'passport-clients',

require('./components/passport/Clients.vue')

);

Vue.component(

'passport-authorized-clients',

require('./components/passport/AuthorizedClients.vue')

);

Vue.component(

'passport-personal-access-tokens',

Page 513: Laravel: Up and Running: A Framework for Building Modern PHP

require('./components/passport/PersonalAccessTokens.vue')

);

constapp=newVue({

el:'body'

});

Younowgetthreecomponentsthatyoucanuseanywhereinyourapplication:

<passport-clients></passport-clients>

<passport-authorized-clients></passport-authorized-clients>

<passport-personal-access-tokens></passport-personal-access-tokens>

<passport-clients>showsyourusersalloftheclientsthey’vecreated.ThismeansSpaceBook’screatorwillseetheSpaceBookclientlistedherewhenshelogsintoSpeakr.

<passport-authorized-clients>showsyourusersalloftheclientsthey’veauthorizedtohaveaccesstotheiraccounts.ThismeansanyusersofbothSpaceBookandSpeakrwhohavegivenSpaceBookaccesstotheirSpeakraccountwillseeSpaceBooklistedhere.

<passport-personal-access-tokens>showsyourusersanypersonalaccesstokensthey’vecreatedhere.ThismeansthecreatorofRaceBook,theSpaceBookcompetitor,willseehispersonalaccesstokenherethathe’sbeenusingtotestouttheSpeakrAPI.

IfyouareonafreshinstallofLaravelandwanttotesttheseout,thereareafewstepstotaketogetitworking:

FollowtheinstructionsgivenearlierinthischaptertogetPassportinstalled.

Inyourterminal,runthefollowingcommands:phpartisanvendor:publish--tag=passport-components

npminstall

gulp

phpartisanmake:auth

Openresources/views/home.blade.phpandaddtheVuecomponentreferences(e.g.,<passport-clients>)justbelowthe<divclass="panel">.

Ifyou’dlike,youcanjustusethosecomponentsastheyare.ButyoucanalsousethemasreferencepointstounderstandhowtousetheAPIandcreateyourownfrontendcomponentsinwhateverformatyou’dlike.

Page 514: Laravel: Up and Running: A Framework for Building Modern PHP

PassportScopesIfyou’refamiliarwithOAuth,youprobablynoticedwehaven’ttalkedaboutscopes.Everythingwe’vecoveredsofarcanbecustomizedbyscope,butfirstlet’squicklycoverwhatscopesare.

InOAuth,scopesaredefinedsetsofprivilegesthataresomethingotherthan“candoeverything.”Ifyou’veevergottenaGitHubAPItokenbefore,forexample,youmight’venoticedthatsomeappswantaccessjusttoyournameandemailaddress,somewantaccesstoallofyourrepos,andsomewantaccesstoyourgists.Eachoftheseisa“scope,”whichallowsboththeuserandtheconsumerapptodefinewhataccesstheconsumerappneedstoperformitsjob.

AsshowninExample13-24,youcandefinethescopesforyourapplicationintheboot()methodofyourAuthServiceProvider.

Example13-24.DefiningPassportscopes//AuthServiceProvider

useLaravel\Passport\Passport;

...

publicfunctionboot()

{

...

Passport::tokensCan([

'list-clips'=>'Listsoundclips',

'add-delete-clips'=>'Addnewanddeleteoldsoundclips',

'admin-account'=>'Administeraccountdetails',

]);

}

Onceyouhaveyourscopesdefined,theconsumerappcandefinewhichscopesit’saskingforaccessto.Justaddaspace-separatedlistoftokensinthe“token”fieldtotheinitialredirect,inthescopefield,asshowninExample13-25.

Example13-25.Requestingauthorizationtoaccessspecificscopes//InSpaceBook'sroutes/web.php:

Route::get('speakr/redirect',function(){

$query=http_build_query([

'client_id'=>config('speakr.id'),

'redirect_uri'=>url('speakr/callback'),

'response_type'=>'code',

'scope'=>'list-clipsadd-delete-clips'

]);

returnredirect('http://speakr.dev/oauth/authorize?'.$query);

});

Whentheusertriestoauthorizewiththisapp,it’llpresentthelistofrequestedscopes.Thisway,theuserwillseethedifferencebetween“SpaceBookisrequestingtoseeyouremailaddress”and“SpaceBookisrequestingaccesstopostasyouanddeleteyourpostsandmessageyourfriends.”

YoucancheckforscopeusingmiddlewareorontheUserinstance.

Page 515: Laravel: Up and Running: A Framework for Building Modern PHP

Example13-26showshowtocheckontheUser.

Example13-26.CheckingwhetherthetokenauserauthenticatedwithcanperformagivenactionRoute::get('/events',function(){

if(auth()->user()->tokenCan('add-delete-clips')){

//

}

});

Therearetwomiddlewareyoucanuseforthistoo,scopeandscopes.Tousetheseinyourapp,addthemto$routeMiddlewareinyourapp/Http/Kernel.phpfile:

'scopes'=>\Laravel\Passport\Http\Middleware\CheckScopes::class,

'scope'=>\Laravel\Passport\Http\Middleware\CheckForAnyScope::class,

YoucannowusethemiddlewareasillustratedinExample13-27.scopesrequiresallofthedefinedscopestobeontheuser ’stokeninorderfortheusertoaccesstheroute,whilescoperequiresatleastoneofthedefinedscopestobeontheuser ’stoken.

Example13-27.Usingmiddlewaretorestrictaccessbasedontokenscopes//routes/api.php

Route::get('clips',function(){

//Accesstokenhasboththe"list-clips"and"add-delete-clips"scopes

})->middleware('scopes:list-clips,add-delete-clips');

//or

Route::get('clips',function(){

//Accesstokenhasatleastoneofthelistedscopes

})->middleware('scope:list-clips,add-delete-clips')

Ifyouhaven’tdefinedanyscopes,theappwilljustworkasiftheydon’texist.Themomentyouusescopes,however,yourconsumerappswillhavetoexplicitlydefinewhichscopesthey’rerequestingaccesswith.Theoneexceptiontothisruleisthatifyou’reusingthepasswordgranttypeyourconsumerappcanrequestthe*scope,whichgivesthetokenaccesstoeverything.

Page 516: Laravel: Up and Running: A Framework for Building Modern PHP

Laravel5.2+APITokenAuthenticationLaravel5.2introducedasimpleAPItokenauthenticationmechanism.It’snotmuchdifferentfromausernameandpassword:there’sasingletokenassignedtoeachuserthatclientscanpassalongwitharequesttoauthenticatethatrequestforthatuser.

ThisAPItokenmechanismisnotnearlyassecureasOAuth2.0,somakesureyouknowit’stherightfitforyourapplicationbeforedecidingtouseit.Butifitis,itcouldn’tbemuchsimplertoimplement.

First,adda60-characteruniqueapi_tokencolumntoyouruserstable:

$table->string('api_token',60)->unique();

Next,updatewhatevermethodcreatesyournewusersandensureitsetsavalueforthisfieldforeachnewuser.Laravelhasahelperforgeneratingrandomstrings,soifyouwanttousethat,justsetthefieldtostr_random(60)foreach.You’llalsoneedtodothisforpreexistingusersifyou’readdingthistoaliveapplication.

Towrapanyrouteswiththisauthenticationmethod,usetheauth:apiroutemiddleware,asinExample13-28.

Example13-28.ApplyingtheAPIauthmiddlewaretoaroutegroupRoute::group(['prefix'=>'api','middleware'=>'auth:api'],function(){

//

});

Notethat,sinceyou’reusinganauthenticationguardotherthanthestandardguard,you’llneedtospecifythatguardanytimeyouuseanyauth()methods:

$user=auth()->guard('api')->user();

Page 517: Laravel: Up and Running: A Framework for Building Modern PHP

TestingFortunately,testingAPIsisactuallysimplerthantestingalmostanythingelseinLaravel.

WecoverthisinmoredepthinChapter12,butthereareaseriesofmethodsformakingassertionsagainstJSON.Combinethatcapabilitywiththesimplicityoffull-stackapplicationtestsandyoucanputtogetheryourAPItestsquicklyandeasily.TakealookatthecommonAPItestingpatterninExample13-29.

Example13-29.AcommonAPItestingpattern...

classDogsApiTestextendsTestCase

{

useWithoutMiddleware,DatabaseMigrations;

publicfunctiontest_it_gets_all_dogs()

{

$this->be(factory(User::class)->create());

$dog1=factory(Dog::class)->create();

$dog2=factory(Dog::class)->create();

$this->visit('api/dogs');

$this->seeJson([

'name'=>$dog1->name

]);

$this->seeJson([

'name'=>$dog2->name

]);

}

}

Notethatwe’reusingWithoutMiddlewaretoavoidworryingabouttheauthentication.You’llwanttotestthatseparately,ifatall(formoreonauthentication,seeChapter9).

Wegenerateauserandauthenticateasthatuserwith$this->be().Wetheninserttwodogsintothedatabase,andthenvisittheAPIrouteforlistingalldogsandmakesurebotharepresentintheoutput.

YoucancoverallofyourAPIroutessimplyandeasilyhere,includingmodifyingactionslikePOSTandPATCH.

Page 518: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRLaravelisgearedtowardbuildingAPIsandmakesitsimpletoworkwithJSONandRESTfulAPIs.Therearesomeconventions,likeforpagination,butmuchofthedefinitionofexactlyhowyourAPIwillbesorted,orauthenticated,orwhateverelseisuptoyou.

Laravelprovidestoolsforauthenticationandtesting,easymanipulationandreadingofheaders,andworkingwithJSON,evenautomaticallyencodingallEloquentresultstoJSONifthey’rereturneddirectlyfromaroute.

LaravelPassportisaseparatepackagethatmakesitsimpletocreateandmanageanOAuthserverinyourLaravelapps.

Page 519: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter14.StorageandRetrieval

WecoveredhowtostoredatainrelationaldatabasesinChapter8,butthere’salotmorethatcanbestored,bothlocallyandremotely.Inthischapterwe’llcoverfilesystemandin-memorystorage,fileuploadsandmanipulation,nonrelationaldatastores,sessions,thecache,cookies,andfull-textsearch.

Page 520: Laravel: Up and Running: A Framework for Building Modern PHP

LocalandCloudFileManagersLaravelprovidesaseriesoffilemanipulationtoolsthroughtheStoragefacade,andafewhelperfunctions.

Laravel’sfilesystemaccesstoolscanconnecttothelocalfilesystemaswellasS3,Rackspace,andFTP.TheS3andRackspacefiledriversareprovidedbyFlysystem,andit’ssimpletoaddadditionalFlysystemproviderstoyourLaravelapp—forexample,DropboxorWebDAV.

Page 521: Laravel: Up and Running: A Framework for Building Modern PHP

ConfiguringFileAccessThedefinitionsforLaravel’sfilemanagerliveinconfig/filesystems.php.Eachconnectioniscalleda“disk,”andExample14-1liststhedisksthatareavailableoutofthebox.

Example14-1.Defaultavailablestoragedisks...

'disks'=>[

'local'=>[

'driver'=>'local',

'root'=>storage_path('app'),

],

'public'=>[

'driver'=>'local',

'root'=>storage_path('app/public'),

'visibility'=>'public',

],

's3'=>[

'driver'=>'s3',

'key'=>'your-key',

'secret'=>'your-secret',

'region'=>'your-region',

'bucket'=>'your-bucket',

],

],

Page 522: Laravel: Up and Running: A Framework for Building Modern PHP
Page 523: Laravel: Up and Running: A Framework for Building Modern PHP

THESTORAGE_PATH()HELPERThestorage_path()helperusedinExample14-1linkstoLaravel’sconfiguredstoragedirectory,storage/.Anythingyoupasstoitisaddedtotheendofthedirectoryname,sostorage_path('public')willreturnthestringstorage/public.

Thelocaldiskconnectstoyourlocalstoragesystemandpresumesitwillbeinteractingwiththeappdirectoryofthestoragepath,whichisstorage/app.

Thepublicdiskisalsoalocaldisk(althoughyoucanchangeitifyou’dlike),whichisintendedforusewithanyfilesyouintendtobeservedbyyourapplication.Itdefaultstothestorage/app/publicdirectory,andifyouwanttousethisdirectorytoservefilestothepublic,you’llneedtoaddasymboliclink(symlink)tosomewherewithinthepublic/directory.Thankfully,there’sanArtisancommandforthat:

#Mapspublic/storagetoservethefilesfromstorage/app/public

phpartisanstorage:link

Thes3diskshowshowLaravelconnectstocloud-basedfilestoragesystems.Ifyou’veeverconnectedtoS3oranyothercloudstorageprovider,thiswillbefamiliar;passityourkeyandsecretandsomeinformationdefiningthe“folder”you’reworkingwith,whichinS3istheregionandthebucket.

Page 524: Laravel: Up and Running: A Framework for Building Modern PHP

UsingtheStorageFacadeInconfig/filesystem.phpyoucansetthedefaultdisk,whichiswhatwillbeusedanytimeyoucalltheStoragefacadewithoutspecifyingadisk.Tospecifyadisk,calldisk('diskname')onthefacade:

Storage::disk('s3')->get('file.jpg');

Thefilesystemseachprovidethefollowingmethods:

get('file.jpg')

Retrievesthefileatfile.jpg

put('file.jpg',$contentsOrStream)

Putsthegivenfilecontentstofile.jpg

putFile('myDir',$file)

Putsthecontentsofaprovidedfile(intheformofaninstanceofeitherIlluminate\Http\FileorIlluminate\Http\UploadedFile)tothemyDirdirectory,butwithLaravelmanagingtheentirestreamingprocessandnamingthefile

exists('file.jpg')

Returnsabooleanofwhetherfile.jpgexists

copy('file.jpg','newfile.jpg')

Copiesfile.jpgtonewfile.jpg

move('file.jpg','newfile.jpg')

Movesfile.jpgtonewfile.jpg

prepend('my.log','logtext')

Addscontentatthebeginningofmy.log

append('my.log','logtext')

Addscontenttotheendofmy.log

delete('file.jpg')

Deletesfile.jpg

deleteDirectory('myDir')

DeletesmyDir

size('file.jpg')

Returnsthesizeinbytesoffile.jpg

lastModified('file.jpg')

ReturnstheUnixtimestampofwhenfile.jpgwaslastmodified

Page 525: Laravel: Up and Running: A Framework for Building Modern PHP

files('myDir')

ReturnsanarrayoffilenamesinthedirectorymyDir

allFiles('myDir')

ReturnsanarrayoffilenamesinthedirectorymyDirandallsubdirectories

directories('myDir')

ReturnsanarrayofdirectorynamesinthedirectorymyDir

allDirectories('myDir')

ReturnsanarrayofdirectorynamesinthedirectorymyDirandallsubdirectories

Page 526: Laravel: Up and Running: A Framework for Building Modern PHP
Page 527: Laravel: Up and Running: A Framework for Building Modern PHP

INJECTINGANINSTANCEIfyou’dpreferinjectinganinstanceinsteadofusingtheFilefacade,typehintorinjectIlluminate\Filesystem\Filesystemandyou’llgetallthesamemethodsavailabletoyou.

Page 528: Laravel: Up and Running: A Framework for Building Modern PHP

AddingAdditionalFlysystemProvidersIfyouwanttoaddanadditionalFlysystemprovider,you’llneedto“extend”Laravel’snativestoragesystem.Inaserviceprovidersomewhere—itcouldbetheboot()methodofAppServiceProvider,butit’dbemoreappropriatetocreateauniqueserviceproviderforeachnewbinding—usetheStoragefacadetoaddnewstoragesystems,asseeninExample14-2.

Example14-2.AddingadditionalFlysytemproviders//Someserviceprovider

publicfunctionboot()

{

Storage::extend('dropbox',function($app,$config){

$client=newDropboxClient(

$config['accessToken'],$config['clientIdentifier']

);

returnnewFilesystem(newDropboxAdapter($client));

});

}

Page 529: Laravel: Up and Running: A Framework for Building Modern PHP

BasicFileUploadsandManipulationOneofthemorecommonusagesfortheStoragefacadeisacceptingfileuploadsfromyourapplication’susers.Let’slookatacommonworkflowforthat,inExample14-3.

Example14-3.Commonuseruploadworkflow...

classDogsController

{

publicfunctionupdatePicture(Request$request,Dog$dog)

{

Storage::put(

'dogs/'.$dog->id,

file_get_contents($request->file('picture')->getRealPath())

);

}

Weput()toafilenameddogs/{id},andwegrabourcontentsfromtheuploadedfile.EveryuploadedfileisadescendantoftheSplFileInfoclass,whichprovidesagetRealPath()methodthatreturnsthepathtothefile’slocation.So,wegetthetemporaryuploadpathfortheuser ’suploadedfile,readitwithfile_get_contents(),andpassitintoStorage::put().

Sincewehavethisfileavailabletoushere,wecandoanythingwewanttothefilebeforewestoreit—useanimagemanipulationpackagetoresizeitifit’sanimage,validateitandrejectitifitdoesn’tmeetourcriteria,orwhateverelsewelike.

IfwewantedtouploadthissamefiletoS3andwehadourcredentialsstoredinconfig/filesystems.php,wecouldjustadjustExample14-3tocallStorage::disk('s3')->put();we’llnowbeuploadingtoS3.TakealookatExample14-4toseeamorecomplexuploadexample.

Example14-4.Amorecomplexexampleoffileuploads,usingIntervention...

classDogsController

{

publicfunctionupdatePicture(Request$request,Dog$dog)

{

$original=$request->file('picture');

//Resizeimagetomaxwidth150

$image=Image::make($original)->resize(150,null,function($constraint){

$constraint->aspectRatio();

})->encode('jpg',75);

Storage::put(

'dogs/thumbs/'.$dog->id,

$image->getEncoded()

);

}

IusedanimagelibrarycalledInterventioninExample14-4justasanexample;youcanuseanylibraryyouwant.Theimportantpointisthatyouhavethefreedomtomanipulatethefileshoweveryouwantbeforeyoustorethem.

Page 530: Laravel: Up and Running: A Framework for Building Modern PHP

USINGSTORE()ANDSTOREAS()ONTHEUPLOADEDFILELaravel5.3introducedtheabilitytostoreanuploadedfileusingthefileitself.LearnmoreinExample6-11.

Page 531: Laravel: Up and Running: A Framework for Building Modern PHP

SessionsSessionstorageistheprimarytoolweuseinwebapplicationstostorestatebetweenpagerequests.Laravel’ssessionmanagersupportssessiondriversusingfiles,cookies,adatabase,MemcachedorRedis,orin-memoryarrays(whichexpireafterthepagerequestandareonlygoodfortests).

Youcanconfigureallofyoursessionsettingsanddriversinconfig/session.php.Youcanchoosewhetherornottoencryptyoursessiondata,selectwhichdrivertouse(fileisthedefault),andspecifymoreconnection-specificdetailslikethelengthofsessionstorageandwhichfilesordatabasetablestouse.Takealookatthesessiondocstolearnaboutspecificdependenciesandsettingsyouneedtoprepareforwhicheverdriveryouchoosetouse.

ThegeneralAPIofthesessiontoolsallowsyoutosaveandretrievedatabasedonindividualkeys:session()->put('user_id')andsession()->get('user_id'),forexample.Makesuretoavoidsavinganythingtoaflashsessionkey,sinceLaravelusesthatinternallyforflash(onlyavailableforthenextpagerequest)sessionstorage.

Page 532: Laravel: Up and Running: A Framework for Building Modern PHP

AccessingtheSessionThemostcommonwaytoaccessthesessionisusingtheSessionfacade:

session()->get('user_id');

Butyoucanalsousethesession()methodonanygivenIlluminateRequestobject,asinExample14-5.

Example14-5.Usingthesession()methodonaRequestobjectRoute::get('dashboard',function(Request$request){

$request->session()->get('user_id');

});

OryoucaninjectaninstanceofIlluminate\Session\Store,asinExample14-6.

Example14-6.InjectingthebackingclassforsessionsRoute::get('dashboard',function(Illuminate\Session\Store$session){

return$session->get('user_id');

});

Finally,youcanusetheglobalsession()helper.Useitwithnoparameterstogetasessioninstance,withasinglestringparameterto“get”fromthesession,orwithanarrayto“put”tothesession,asdemonstratedinExample14-7.

Example14-7.Usingtheglobalsession()helper//get

$value=session()->get('key');

$value=session('key');

//put

session()->put('key','value');

session(['key','value']);

Ifyou’renewtoLaravelandnotsurewhichtouse,I’drecommendusingtheglobalhelper.

Page 533: Laravel: Up and Running: A Framework for Building Modern PHP

TheMethodsAvailableonSessionInstancesThetwomostcommonmethodsareget()andput(),butlet’stakealookateachoftheavailablemethodsandtheirparameters:

session()->get($key,$fallbackValue)

get()pullsthevalueoftheprovidedkeyoutofthesession.Ifthereisnovalueattachedtothatkey,itwillreturnthefallbackvalueinstead(andifyoudon’tprovideafallback,itwillreturnnull).Thefallbackvaluecanbeastringoraclosure,asyoucanseeinthefollowingexamples.

$points=session()->get('points');

$points=session()->get('points',0);

$points=session()->get('points',function(){

return(newPointGetterService)->getPoints();

});

session()->put($key,$value)

put()storestheprovidedvalueinthesessionattheprovidedkey:

session()->put('points',45);

$points=session()->get('points');

session()->push($key,$value)

Ifanyofyoursessionvaluesarearrays,youcanusepush()toaddavalueontothearray:

session()->put('friends',['Saúl','Quang','Mechteld']);

session()->push('friends','Javier');

session()->has($key)

has()checkswhetherthere’savaluesetattheprovidedkey:

if(session()->has('points')){

//dosomething

}

Youcanalsopassanarrayofkeys,anditonlyreturnstrueifallofthekeysexist.

Page 534: Laravel: Up and Running: A Framework for Building Modern PHP
Page 535: Laravel: Up and Running: A Framework for Building Modern PHP

SESSION()->HAS()ANDNULLVALUESIfasessionvalueisset,butthevalueisnull,session()->has()willreturnfalse.

session()->all()

all()returnsanarrayofeverythingthat’sinthesession,includingthosevaluessetbytheframework.You’lllikelyseevaluesunderkeyslike_token(CSRFtokens),_previous(previouspage,forback()redirects),andflash(forflashstorage).

session()->forget($key)andsession()->flush()forget()removesapreviouslysetsessionvalue.flush()removeseverysessionvalue,eventhosesetbytheframework:

session()->put('a','awesome');

session()->put('b','bodacious');

session()->forget('a');

//aisnolongerset,bisstillset

session()->flush();

//sessionisnowempty

session()->pull($key,$fallbackValue)

pull()isthesameasget(),exceptthatitdeletesthevaluefromthesessionafterpullingit.

session()->regenerate()

It’snotcommon,butifyouneedtoregenerateyoursessionID,regenerate()isthereforyou.

Page 536: Laravel: Up and Running: A Framework for Building Modern PHP

FlashSessionStorageTherearethreemoremethodswehaven’tcoveredyet,andtheyallhavetodowithsomethingcalled“flash”sessionstorage.

Oneverycommonpatternforsessionstorageistosetavaluethatyouonlywantavailableforthenextpageload.Forexample,youmightwanttostoreamessagelike“Updatedpostsuccessfully.”Youcouldmanuallygetthatmessageandthenwipeitonthenextpageload,butifyouusethispatternalotitcangetwasteful.Enterflashsessionstorage:keysthatareexpectedtoonlylastforasinglepagerequest.

Laravelhandlestheworkforyou,andallyouneedtodoisuseflash()insteadofput().Theusefulmethodshereare:

session()->flash($key,$value)

flash()setsthesessionkeytotheprovidedvalueforjustthenextpagerequest.

session()->reflash()andsession()->keep($key)Ifyouneedthepreviouspage’sflash()sessiondatatostickaroundforonemorerequest,youcanusereflash()torestoretheentireflashcontentsforthenextrequestorkeep($key)tojustrestoreasingleflashvalueforthenextrequest.keep()canalsoacceptanarrayofkeystoreflash.

Page 537: Laravel: Up and Running: A Framework for Building Modern PHP

CacheCachesarestructuredverysimilarlytosessions.YouprovideakeyandLaravelstoresitforyou.Thebiggestdifferenceisthatthedatainacacheiscachedperapplicationandthedatainasessioniscachedperuser.Thatmeanscachesaremorecommonlyusedforstoringlargedatabaseresults,APIcalls,orotherslowqueriesthatcanstandtogetalittlebit“stale.”

Thecacheconfigurationsettingsareavailableatconfig/cache.php.Justlikewithasession,youcansetthespecificconfigurationdetailsforanyofyourdrivers,andalsochoosewhichwillbeyourdefault.Laravelusesthefilecachedriverbydefault,butyoucanalsouseMemcachedorRedis,APC,oradatabase,orwriteyourowncachedriver.Takealookatthecachedocstolearnaboutspecificdependenciesandsettingsyouneedtoprepareforwhicheverdriveryouchoosetouse.

Page 538: Laravel: Up and Running: A Framework for Building Modern PHP

AccessingtheCacheJustlikewithsessions,thereareafewdifferentwaystoaccessacache.Youcanusethefacade:

$users=Cache::get('users');

Youcangetaninstancefromthecontainer,asinExample14-8.

Example14-8.InjectinganinstanceofthecacheRoute::get('users',function(Illuminate\Contracts\Cache\Repository$cache){

return$cache->get('users');

});

Oryoucanusetheglobalcache()helper(introducedinLaravel5.3),asinExample14-9.

Example14-9.Usingtheglobalcache()helper//getfromcache

$users=cache('key','defaultvalue');

$users=cache()->get('key','defaultvalue');

//putfor$minutesduration

$users=cache(['key'=>'value'],$minutes);

$users=cache()->put('key','value',$minutes);

Ifyou’renewtoLaravelandnotsurewhichtouse,I’drecommendusingtheglobalhelper.

Page 539: Laravel: Up and Running: A Framework for Building Modern PHP

TheMethodsAvailableonCacheInstancesLet’stakealookatthemethodsyoucancallonaCacheinstance:

cache()->get($key,$fallbackValue)andcache()->pull($key,$fallbackValue)

get()makesiteasytoretrievethevalueforanygivenkey.pull()isthesameasget()exceptitremovesthecachedvalueafterretrievingit.

cache()->put($key,$value,$minutesOrExpiration)

put()setsthevalueofthespecifiedkeyforagivennumberofminutes.Ifyou’dprefersettinganexpirationdate/timeinsteadofanumberofminutes,youcanpassaCarbonobjectasthethirdparameter:

cache()->put('key','value',Carbon::now()->addDay());

cache()->add($key,$value)

add()issimilartoput(),exceptifthevaluealreadyexists,itwon’tsetit.Also,themethodreturnsabooleanofwhetherornotthevaluewasactuallyadded:

$someDate=Carbon::now();

cache()->add('someDate',$someDate);//returnstrue

$someOtherDate=Carbon::now()->addHour();

cache()->add('someDate',$someOtherDate);//returnsfalse

cache()->forever($key,$value)

forever()savesavaluetothecacheforaspecifickey;it’sthesameasput(),exceptthevalueswillneverexpire(untilthey’reremovedwithforget()).

cache()->has($key)

has()returnsabooleanofwhetherornotthere’savalueattheprovidedkey.

cache()->remember($key,$minutes,$closure)andcache()->rememberForever($key,$closure)

Page 540: Laravel: Up and Running: A Framework for Building Modern PHP

remember()providesasinglemethodtohandleaverycommonflow:lookupwhetheravalueexistsinthecacheforacertainkey,andifitdoesn’t,getthatvaluesomehow,saveittothecache,andreturnit.remember()letsyouprovideakeytolookup,thenumberofminutesitshouldbesavedfor,andaclosuretodefinehowtolookitup,incasethekeyhasnovalueset.rememberForever()isthesame,exceptitdoesn’tneedyoutosetthenumberofminutesitshouldexpireafter.Takealookatthefollowingexampletoseeacommonuserscenarioforremember():

//Eitherreturnsthevaluecachedat"users"orgets"User::all()",

//cachesitat"users",andreturnsit

$users=cache()->remember('users',120,function(){

returnUser::all();

});

cache()->increment($key,$amount)andcache()->decrement($key,$amount)increment()anddecrement()allowyoutoincrementanddecrementintegervaluesinthecache.Ifthereisnovalueatthegivenkey,it’llbetreatedasifitwere0,andifyoupassasecondparametertoincrementordecrement,it’llincrementordecrementbythatamountinsteadofby1.

cache()->forget($key)andcache()->flush()forget()worksjustlikeSession’sforget()method:passitakeyandit’llwipethatkey’svalue.flush()wipestheentirecache.

Page 541: Laravel: Up and Running: A Framework for Building Modern PHP

CookiesYoumightexpectcookiestoworkthesameasthesessionandthecache.Afacadeandaglobalhelperareavailableforthesetoo,andourmentalmodelsofallthreearesimilar:youcangetorsettheirvaluesinthesameway.

Butbecausecookiesareinherentlyattachedtotherequestsandresponses,you’llneedtointeractwithcookiesdifferently.Let’slookreallybrieflyatwhatmakescookiesdifferent.

Page 542: Laravel: Up and Running: A Framework for Building Modern PHP

CookiesinLaravelCookiescanexistinthreeplacesinLaravel.Theycancomeinviatherequest,whichmeanstheuserhadthecookiewhenshevisitedthepage.YoucanreadthatwiththeCookiefacade,oryoucanreaditoffoftherequestobject.

Theycanalsobesentoutwitharesponse,whichmeanstheresponsewillinstructtheuser ’sbrowsertosavethecookieforfuturevisits.Youcandothisbyaddingthecookietoyourresponseobjectbeforereturningit.

Andlastly,acookiecanbequeued.IfyouusetheCookiefacadetosetacookie,youhaveputitintoa“CookieJar”queue,anditwillberemovedandaddedtotheresponseobjectbytheAddQueuedCookiesToResponsemiddleware.

Page 543: Laravel: Up and Running: A Framework for Building Modern PHP

AccessingtheCookieToolsYoucangetandsetcookiesinthreeplaces:theCookiefacade,thecookie()globalhelper,andtherequestandresponseobjects.

TheCookiefacadeTheCookiefacadeisthemostfull-featuredoption,allowingyoutonotonlyreadandmakecookies,butalsotoqueuethemtobeaddedtotheresponse.Itprovidesthefollowingmethods:

Cookie::get($key)

Topullthevalueofacookiethatcameinwiththerequest,youcanjustrunCookie::get('cookie-name').Thisisthesimplestoption.

Cookie::has($key)

YoucancheckwhetheracookiecameinwiththerequestusingCookie::has('cookie-name'),whichreturnsaboolean.

Cookie::make(...params)

Ifyouwanttomakeacookiewithoutqueueingitanywhere,youcanuseCookie::make().Themostlikelyuseforthiswouldbetomakeacookieandthenmanuallyattachittotheresponseobject,whichwe’llcoverinabit.Herearetheparametersformake(),inorder:

$nameisthenameofthecookie

$valueisthecontentofthecookie

$minutesspecifieshowmanyminutesthecookieshouldlive

$pathisthepathunderwhichyourcookieshouldbevalid

$domainliststhedomainsforwhichyourcookieshouldwork

$secureindicateswhetherthecookieshouldonlybetransmittedoverasecure(HTTPS)connection

$httpOnlyindicateswhetherthecookiewillbemadeaccessibleonlythroughtheHTTPprotocol

Cookie::make()

ReturnsaninstanceofSymfony\Component\HttpFoundation\Cookie.

Page 544: Laravel: Up and Running: A Framework for Building Modern PHP
Page 545: Laravel: Up and Running: A Framework for Building Modern PHP

DEFAULTSETTINGSFORCOOKIESTheCookieJarthattheCookiefacadeinstanceusesreadsitsdefaultsfromthesessionconfig.So,ifyouchangeanyoftheconfigurationvaluesforthesessioncookieinconfig/session.php,thosesamedefaultswillbeappliedtoallofyourcookiesthatyoucreateusingtheCookiefacade.

Cookie::queue(Cookie||...params)

IfyouuseCookie::make(),you’llstillneedtoattachthecookietoyourresponse,whichwe’llcovershortly.Cookie::queue()hasthesamesyntaxasCookie::make(),butitenqueuesthecreatedcookietobeautomaticallyattachedtotheresponsebymiddleware.Ifyou’dlike,youcanalsojustpassacookieyou’vecreatedyourselfintoCookie::queue().Here’sthesimplestpossiblewaytoaddacookietotheresponseinLaravel:

Cookie::queue('dismissed-popup',true,15);

Page 546: Laravel: Up and Running: A Framework for Building Modern PHP
Page 547: Laravel: Up and Running: A Framework for Building Modern PHP

WHENYOURQUEUEDCOOKIESWON’TGETSETCookiescanonlybereturnedasapartofaresponse.So,ifyouenqueuecookieswiththeCookiefacadeandthenyourresponseisn’treturnedcorrectly—forexample,ifyouusePHP’sexit()orsomethinghaltstheexecutionofyourscript—yourcookieswon’tbeset.

Thecookie()globalhelperThecookie()globalhelperwillreturnaCookieJarinstanceifyoucallitwithnoparameters.However,twoofthemostconvenientmethodsontheCookiefacade—has()andget()—existonlyonthefacade,notontheCookieJar.So,inthiscontext,Ithinktheglobalhelperisactuallylessusefulthantheotheroptions.

Theonetaskforwhichthecookie()globalhelperisusefuliscreatingacookie.Ifyoupassparameterstocookie(),they’llbepasseddirectlytotheequivalentofCookie::make(),sothisisthefastestwaytocreateacookie:

$cookie=cookie('dismissed-popup',true,15);

Page 548: Laravel: Up and Running: A Framework for Building Modern PHP
Page 549: Laravel: Up and Running: A Framework for Building Modern PHP

INJECTINGANINSTANCEYoucanalsoinjectaninstanceofIlluminate\Cookie\CookieJaranywhereintheapp,butyou’llhavethesamelimitationsdiscussedhere.

CookiesonrequestandresponseobjectsSincecookiescomeinasapartoftherequestandaresetasapartoftheresponse,thoseIlluminateobjectsaretheplacestheyactuallylive.TheCookiefacade’sget(),has(),andqueue()methodsarejustproxiestointeractwiththerequestandresponseobjects.

So,thesimplestwaytointeractwithcookiesistopullcookiesfromtherequestandsetthemontheresponse.

ReadingcookiesfromrequestobjectsOnceyouhaveacopyofyourrequestobject—ifyoudon’tknowhowtogetone,justtryapp('request')—youcanusetherequestobject’scookie()methodtoreaditscookies,asshowninExample14-10.

Example14-10.ReadingacookiefromarequestobjectRoute::get('dashboard',function(Illuminate\Http\Request$request){

$userDismissedPopup=$request->cookie('dismissed-popup',false);

});

Asyoucanseeinthisexample,thecookie()methodhastwoparameters:thecookie’snameand,optionally,thefallbackvalue.

SettingcookiesonresponseobjectsWheneveryouhaveyourresponseobjectready,youcanusethecookie()method(orthewithCookie()methodinLaravelpriorto5.3)onittoaddacookietotheresponse,likeinExample14-11.

Example14-11.SettingacookieonaresponseobjectRoute::get('dashboard',function(){

$cookie=cookie('saw-dashboard',true);

returnResponse::view('dashboard')

->cookie($cookie);

});

Ifyou’renewtoLaravelandnotsurewhichoptiontouse,I’drecommendsettingcookiesontherequestandresponseobjects.It’sabitmorework,butwillleadtofewersurprisesiffuturedevelopersdon’tunderstandtheCookieJarqueue.

Page 550: Laravel: Up and Running: A Framework for Building Modern PHP

Full-TextSearchwithLaravelScoutLaravelScoutisaseparatepackagethatyoucanbringintoyourLaravelappstoaddfull-textsearchtoyourEloquentmodels.ScoutmakesiteasytoindexandsearchthecontentsofyourEloquentmodels;itshipswithAlgoliaandElasticsearchdrivers,buttherearealsocommunitypackagesforotherproviders.I’llassumeyou’reusingAlgolia.

Page 551: Laravel: Up and Running: A Framework for Building Modern PHP

InstallingScoutFirst,pullinthepackageinanyLaravel5.3+app:

composerrequirelaravel/scout

Next,addLaravel\Scout\ScoutServiceProvider::class,totheproviderssectionofconfig/app.php.

You’llwanttosetupyourScoutconfiguration.Runphpartisanvendor:publishandpasteyourAlgoliacredentialsinconfig/scout.php.

Finally,installtheAlgoliaSDK:

composerrequirealgolia/algoliasearch-client-php

Page 552: Laravel: Up and Running: A Framework for Building Modern PHP

MarkingYourModelforIndexingInyourmodel(we’lluseReview,forabookreview,forthisexample),importtheLaravel\Scout\Searchabletrait.

YoucandefinewhichpropertiesaresearchableusingthetoSearchableArray()method(itdefaultstomirroringtoArray()),anddefinethenameofthemodel’sindexusingthesearchableAs()method(itdefaultstothetablename).

Scoutsubscribestothecreate/delete/updateeventsonyourmarkedmodels.Whenyoucreate,update,ordeleteanyrows,ScoutwillsyncthosechangesuptoAlgolia.It’lleithermakethosechangessynchronouslywithyourupdatesor,ifyouconfigureScouttouseaqueue,queuetheupdates.

Page 553: Laravel: Up and Running: A Framework for Building Modern PHP

SearchingYourIndexScout’ssyntaxissimple.Forexample,tofindanyReviewwiththewordLlewinit:

Review::search('Llew')->get();

YoucanalsomodifyyourqueriesasyouwouldwithregularEloquentcalls:

//GetallrecordsfromtheReviewthatmatchtheterm"Llew",

//limitedto20perpageandreadingthepagequeryparameter,

//justlikeEloquentpagination

Review::search('Llew')->paginate(20);

//GetallrecordsfromtheReviewthatmatchtheterm"Llew"

//andhavetheaccount_idfieldsetto2

Review::search('Llew')->where('account_id',2)->get();

Whatcomesbackfromthesesearches?AcollectionofEloquentmodels,rehydratedfromyourdatabase.TheIDsarestoredinAlgolia,whichreturnsalistofmatchedIDs;ScoutthenpullsthedatabaserecordsforthoseandreturnsthemasEloquentobjects.

Youdon’thavefullaccesstothecomplexityofSQLWHEREcommands,butitprovidesabasicframeworkforcomparisoncheckslikeyoucanseeinthecodesampleshere.

Page 554: Laravel: Up and Running: A Framework for Building Modern PHP

QueuesandScoutAtthispointyourappwillbemakingHTTPrequeststoAlgoliaoneveryrequestthatmodifiesanydatabaserecords.Thiscanslowdownyourapplicationquickly,whichiswhyScoutmakesiteasytopushallofitsactionsontoaqueue.

Inconfig/scout.php,setqueuetotruesothattheseupdatesaresettobeindexedasynchronously.Yourfull-textindexisnowoperatingunder“eventualconsistency”;yourdatabaserecordswillreceivetheupdatesimmediately,andtheupdatestoyoursearchindexeswillbequeuedandupdatedasfastasyourqueueworkerallows.

Page 555: Laravel: Up and Running: A Framework for Building Modern PHP

PerformOperationsWithoutIndexingIfyouneedtoperformasetofoperationsandavoidtriggeringtheindexinginresponse,wraptheoperationsinthewithoutSyncingToSearch()methodonyourmodel:

Review::withoutSyncingToSearch(function(){

//makeabunchofreviews,e.g.

factory(Review::class,10)->create();

});

Page 556: Laravel: Up and Running: A Framework for Building Modern PHP

ManuallyTriggerIndexingviaCodeIfyouwanttomanuallytriggerindexingyourmodel,youcandoitusingcodeinyourapporviathecommandline.

Tomanuallytriggerindexingfromyourcode,addsearchable()totheendofanyEloquentqueryanditwillindexalloftherecordsthatwerefoundinthatquery:

Review::all()->searchable();

Youcanalsochoosetoscopethequerytoonlythoseyouwanttoindex.However,Scoutissmartenoughtoinsertnewrecordsandupdateoldrecords,soyoumaychoosetojustreindextheentirecontentsofthemodel’sdatabasetable.

Youcanalsorunsearchable()onrelationshipmethods:

$user->reviews()->searchable();

Ifyouwanttounindexanyrecordswiththesamesortofquerychaining,justuseunsearchable()instead:

Review::where('sucky',true)->unsearchable();

Page 557: Laravel: Up and Running: A Framework for Building Modern PHP

ManuallyTriggerIndexingviatheCLIYoucanalsotriggerindexingwithanArtisancommand:

phpartisanscout:importApp\\Review

ThiswillchunkalloftheReviewmodelsandindexthemall.

Page 558: Laravel: Up and Running: A Framework for Building Modern PHP

TestingTestingmostofthesefeaturesisassimpleasjustusingtheminyourtests;noneedtomockorstub.Thedefaultconfigurationwillalreadywork—forexample,takealookatphpunit.xmltoseethatyoursessiondriverandcachedriverhavebeensettovaluesappropriatefortests.

However,thereareafewconveniencemethodsandafewgotchasthatyoushouldknowaboutbeforeyouattempttotestthemall.

Page 559: Laravel: Up and Running: A Framework for Building Modern PHP

FileStorageTestingfileuploadscanbeabitofapain,butfollowthesestepsanditwillbeclear.

UploadingfakefilesFirst,let’slookathowtomanuallycreateaSymfonyUploadedFileobjectforuseinourapplicationtesting(Example14-12).Notethatthisassumeswehaveastorage/testsdirectorywherewe’replacingafilenamedfor-tests.jpgthatwe’lluseforourtests.

Example14-12.CreatingafakeUploadedFilefortestingpublicfunctiontest_file_should_be_stored()

{

$path=storage_path('tests/for-tests.jpg');

$file=newUploadedFile(

$path,//filepath

'for-tests.jpg',//originalfilename

'image/jpg',//MIMEtype

filesize($path),//filesize;besttogetonce&hardcodeintoyourtest,

null,//errorcode

true//whetherwe'reintestmode

);

$this->call('post','upload-route',[],[],['upload'=>$file]);

$this->assertResponseOk();

}

We’vecreatedanewinstanceofUploadedFilethatreferstoourtestingfile,andwecannowuseittotestourroutes.

ReturningfakefilesIfyourrouteisexpectingarealfiletoexist,sometimesthebestwaytomakeittestableistomakethatrealfileactuallyexist.Let’ssayeveryusermusthaveaprofilepicture.

First,let’ssetupthemodelfactoryfortheusertouseFakertomakeacopyofthepicture,asinExample14-13.

Example14-13.ReturningfakefileswithFaker$factory->define(User::class,function(Faker\Generator$faker){

return[

'picture'=>$faker->file(

storage_path('tests'),//sourcedirectory

storage_path('app'),//targetdirectory

false//returnjustfilename,notfullpath

),

'name'=>$faker->name,

];

});

Faker ’sfile()methodpicksarandomfilefromthesourcedirectoryandcopiesittothetargetdirectory,andthenreturnsthefilename.Sowe’vejustpickedarandomfilefromthestorage/testsdirectory,copiedittothestorage/appdirectory,andsetitsfilenameasthepicturepropertyonourUser.AtthispointwecanuseaUserintestsonroutesthatexpecttheUsertohaveapicture,asseeninExample14-14.

Page 560: Laravel: Up and Running: A Framework for Building Modern PHP

Example14-14.Assertingthatanimage’sURLisechoedpublicfunctiontest_user_profile_picture_echoes_correctly()

{

$user=factory(User::class)->create();

$this->visit("users/{$user->id}");

$this->see($user->picture);

}

Ofcourse,inmanycontextsyoucanjustgeneratearandomstringtherewithoutevencopyingafile.Butifyourroutescheckforthefile’sexistenceorrunanyoperationsonthefile,thisisyourbestoption.

Page 561: Laravel: Up and Running: A Framework for Building Modern PHP

SessionIfyouneedtoassertsomethinghasbeensetinthesession,youcanusesomeconveniencemethodsLaravelmakesavailableineverytest.Allofthesemethodsareavailableinyourtestsonthe$thisobject:

assertSessionHas($key,$value=null)

Assertsthatthesessionhasavalueforaparticularkey,and,ifthesecondparameterispassed,thatthatkeyisaparticularvalue:

publicfunctiontest_some_thing()

{

//dostuff

$this->assertSessionHas('key','value');

}

assertSessionHasAll(array$bindings)

Ifpassedanarrayofkey/valuepairs,assertsthatallofthekeysareequaltoallofthevalues.Ifoneormoreofthearrayentriesisjustavalue(withPHP’sdefaultnumerickey),itwilljustbecheckedforexistenceinthesession:

$check=[

'has',

'hasWithThisValue'=>'thisValue',

]

$this->assertSessionHasAll($check);

assertSessionMissing($key)

Assertsthatthesessiondoesnothaveavalueforaparticularkey.

assertSessionHasErrors($bindings=[],$format=null)

Assertsthatthesessionhasanerrorsvalue.ThisisthekeyLaravelusestosenderrorsbackfromvalidationfailures.Ifthearraycontainsjustkeys,itwillcheckthaterrorsaresetwiththosekeys:

$this->post('test-route',['failing'=>'data']);

$this->assertSessionHasErrors(['name','email']);

Youcanalsopassvaluesforthosekeys,andoptionallya$format,tocheckthatthemessagesforthoseerrorscamebackthewayyouexpected:

$this->post('test-route',['failing'=>'data']);

$this->assertSessionHasErrors([

'email'=>'<strong>Theemailfieldisrequired.</strong>'

],'<strong>:message</strong>');

assertHasOldInput()

Page 562: Laravel: Up and Running: A Framework for Building Modern PHP

Sinceyoucanflashthepreviouspage’sinputtothesession,youmaywanttoassertthatit’sbeenflashedcorrectly:

$this->post('test-route',['failing'=>'data']);

$this->assertHasOldInput();

Page 563: Laravel: Up and Running: A Framework for Building Modern PHP

CacheThere’snothingspecialabouttestingyourfeaturesthatusecache—justdoit:

Cache::put('key','value',15);

$this->assertEquals('value',Cache::get('key'));

Laravelusesthe“array”cachedriverbydefaultinyourtestingenvironment,whichjuststoresyourcachevaluesinmemory.

Page 564: Laravel: Up and Running: A Framework for Building Modern PHP

CookiesIfyouneedtosetacookiebeforetestingarouteinyourapplicationtests,youcanmanuallypasscookiestooneoftheparametersofthecall()method.Tolearnmoreaboutcall(),checkoutChapter12.

Page 565: Laravel: Up and Running: A Framework for Building Modern PHP

EXCLUDINGYOURCOOKIEFROMENCRYPTIONDURINGTESTING

Yourcookieswon’tworkinyourtestsunlessyouexcludethemfromLaravel’scookieencryptionmiddleware.YoucandothisbyteachingtheEncryptCookiesmiddlewaretotemporarilydisableitselfforthatcookie:

useIlluminate\Cookie\Middleware\EncryptCookies;

...

$this->app->resolving(

EncryptCookies::class,

function($object){

$object->disableFor('cookie-name');

}

);

//...runtest

ThatmeansyoucansetandcheckagainstacookiewithsomethinglikeExample14-15.

Example14-15.Runningunittestsagainstcookiespublicfunctiontest_cookie()

{

$this->app->resolving(EncryptCookies::class,function($object){

$object->disableFor('my-cookie');

});

$this->call('get','route-echoing-my-cookie-value',[],['my-cookie'=>'baz']);

$this->see('baz');

}

If,forsomereason,you’drathernotdisableencryption,youcaninsteadsettheencryptedvalueofthecookielikeinExample14-16.

Example14-16.ManuallyencryptingacookiebeforesettingituseIlluminate\Contracts\Encryption\Encrypter;

...

publicfunctiontest_cookie()

{

$encryptedBaz=app(Encrypter::class)->encrypt('baz');

$this->call(

'get',

'route-echoing-my-cookie-value',

[],

['my-cookie'=>$encryptedBaz]

);

$this->see('baz');

}

Ifyouwanttotestthataresponsehasacookieset,youcanuseeitherseeCookie()totestforthecookie:

$this->visit('cookie-setting-route');

$this->seeCookie('cookie-name');

Page 566: Laravel: Up and Running: A Framework for Building Modern PHP

orseePlainCookie()totestforthecookieandtoassertthatit’snotencrypted.

Page 567: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRLaravelprovidessimpleinterfacestomanycommonstorageoperations:filesystemaccess,sessions,cookies,thecache,andsearch.EachoftheseAPIsisthesameregardlessofwhichprovideryouuse,whichLaravelenablesbyallowingmultiple“drivers”toservethesamepublicinterface.Thismakesitsimpletoswitchprovidersdependingontheenvironment,orastheneedsoftheapplicationchange.

Page 568: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter15.MailandNotifications

Sendinganapplication’susersnotificationsviaemail,Slack,SMS,oranothernotificationsystemisacommonbutsurprisinglycomplexrequirement.Laravel’smailandnotificationfeaturesprovideconsistentAPIsthatabstractawaytheneedtopaytoocloseattentiontoanyparticularprovider.JustlikeinChapter14,you’llwriteyourcodeonceandchooseattheconfigurationlevelwhichprovideryou’llusetosendyouremailornotifications.

Page 569: Laravel: Up and Running: A Framework for Building Modern PHP

MailLaravel’smailfunctionalityisaconveniencelayerontopofSwiftMailer,andoutoftheboxLaravelcomeswithdriversforMailgun,Mandrill,Sparkpost,SES,SMTP,PHPMail,andSendmail.

Forallofthecloudservices,you’llsetyourauthenticationinformationinconfig/services.php.However,ifyoutakealookyou’llseetherearealreadykeysthere—andinconfig/mail.php—thatallowyoutocustomizeyourapplication’smailfunctionalityin.envusingvariableslikeMAIL_DRIVERandMAILGUN_SECRET.

Page 570: Laravel: Up and Running: A Framework for Building Modern PHP
Page 571: Laravel: Up and Running: A Framework for Building Modern PHP

CLOUD-BASEDAPIDRIVERDEPENDENCIESIfyou’reusinganyofthecloud-basedAPIdrivers,you’llneedtobringGuzzleinwithComposer.Youcanrunthefollowingcommandtoaddit:

composerrequireguzzlehttp/guzzle:"~5.3|~6.0"

IfyouusetheSESdriver,you’llneedtorunthefollowingcommand:

composerrequireaws/aws-sdk-php:~3.0

Page 572: Laravel: Up and Running: A Framework for Building Modern PHP

“Classic”MailTherearetwodifferentsyntaxesinLaravelforsendingmail:classicandmailable.Themailablesyntaxisthepreferredsyntaxfrom5.3onward,sowe’regoingtofocusonthatinthisbook.Butforthosewhoareworkingin5.1or5.2,here’saquicklookathowtheclassicsyntax(Example15-1)works.

Example15-1.Basic“classic”mailsyntaxMail::send(

'emails.assignment',

['trainer'=>$trainer,'trainee'=>$trainee],

function($m)use($trainer,$trainee){

$m->from($trainer->email,$trainer->name);

$m->to($trainee->email,$trainee->name)->subject('ANewAssignment!');

}

);

ThefirstparameterofMail::send()isthenameoftheview.Remember,emails.assignmentmeansresources/views/emails/assignment.blade.phporresources/views/emails/assignment.php.

Thesecondparameterisanarrayofdatathatyouwanttopasstotheview.

Thethirdparameterisaclosure,inwhichyoudefinehowandwheretosendtheemail:from,to,CC,BCC,subject,andanyothermetadata.Makesuretouseanyvariablesyouwantaccesstowithintheclosure.Andnotethattheclosureispassedoneparameter,whichwe’venamed$m;thisisthemessageobject.

Takealookattheolddocstolearnabouttheclassicmailsyntax.

Page 573: Laravel: Up and Running: A Framework for Building Modern PHP

Basic“Mailable”MailUsageLaravel5.3introducedanewmailsyntaxcalledthe“mailable.”Itworksthesameastheclassicmailsyntax,butinsteadofdefiningyourmailmessagesinaclosure,youinsteadcreateaspecificPHPclasstorepresenteachmail.

Tomakeamailable,usethemake:mailArtisancommand:

phpartisanmake:mailAssignment

Example15-2showswhatthatclasslookslike.

Example15-2.AnautogeneratedmailablePHPclass<?php

namespaceApp\Mail;

useIlluminate\Bus\Queueable;

useIlluminate\Mail\Mailable;

useIlluminate\Queue\SerializesModels;

useIlluminate\Contracts\Queue\ShouldQueue;

classAssignmentextendsMailable

{

useQueueable,SerializesModels;

/**

*Createanewmessageinstance.

*

*@returnvoid

*/

publicfunction__construct()

{

//

}

/**

*Buildthemessage.

*

*@return$this

*/

publicfunctionbuild()

{

return$this->view('view.name');

}

}

Thisclassprobablylooksfamiliar—it’sshapedalmostthesameasaJob.ItevenimportstheQueuabletraitforqueuingyourmailandtheSerializesModelstraitsoanyEloquentmodelsyoupasstotheconstructorwillbeserializedcorrectly.

So,howdoesthiswork?Thebuild()methodonamailableiswhereyou’regoingtodefinewhichviewtouse,whatthesubjectis,andanythingelseyouwanttotweakaboutthemailexceptwhoit’sgoingto.Theconstructoristheplacewhereyou’llpassinanydata,andanypublicpropertiesonyourmailableclasswillbeavailabletothetemplate.

TakealookatExample15-3toseehowwemightupdatetheautogeneratedmailableforourassignmentexample.

Page 574: Laravel: Up and Running: A Framework for Building Modern PHP

Example15-3.Asamplemailable<?php

namespaceApp\Mail;

useIlluminate\Bus\Queueable;

useIlluminate\Mail\Mailable;

useIlluminate\Queue\SerializesModels;

useIlluminate\Contracts\Queue\ShouldQueue;

classAssignmentextendsMailable

{

useQueueable,SerializesModels;

public$trainer;

public$trainee;

publicfunction__construct($trainer,$trainee)

{

$this->trainer=$trainer;

$this->trainee=$trainee;

}

publicfunctionbuild()

{

return$this->subject('Newassignmentfrom'.$this->trainer->name)

->view('emails.assignment');

}

}

Example15-4showshowtosendamailable.

Example15-4.Afewwaystosendmailables//Simplesend

Mail::to($user)->send(newAssignment($trainer,$trainee));

//WithCC/BCC/etc.

Mail::to($user1))

->cc($user2)

->bcc($user3)

->send(newAssignment($trainer,$trainee));

//Withcollections

Mail::to('[email protected]')

->bcc(User::all())

->send(newAssignment($trainer,$trainee))

Page 575: Laravel: Up and Running: A Framework for Building Modern PHP

MailTemplatesMailtemplatesarejustlikeanyothertemplate.Theycanextendothertemplates,usesections,parsevariables,containconditionalorloopingdirectives,anddoanythingelseyoucandoinanormalBladeview.

TakealookatExample15-5toseeapossibleemails.assignmentstemplateforExample15-3.

Example15-5.Sampleassignmentemailtemplate<!--resources/views/emails/assignment.blade.php-->

<p>Hey{{$trainee->name}}!</p>

<p>Youhavereceivedanewtrainingassignmentfrom<b>{{$trainer->name}}</b>.

Checkoutyour<ahref="{{route('training-dashboard')}}">training

dashboard</a>now!</p>

InExample15-3,both$trainerand$traineearepublicpropertiesonyourmailable,whichmakesthemavailabletothetemplate.

Ifyouwanttoexplicitlydefinewhichvariablesarepassedtothetemplate,youcanchainthewith()methodontoyourbuild()callasinExample15-6.

Example15-6.Customizingthetemplatevariablespublicfunctionbuild()

{

return$this->subject('Youhaveanewassignment!')

->view('emails.assignment')

->with(['assignment'=>$this->event->name]);

}

Page 576: Laravel: Up and Running: A Framework for Building Modern PHP
Page 577: Laravel: Up and Running: A Framework for Building Modern PHP

HTMLVERSUSPLAIN-TEXTEMAILSSofarwe’veusedtheview()methodinourbuild()callstacks.Thisexpectsthetemplatewe’rereferencingtopassbackHTML.Ifyou’dliketopassaplain-textversion,thetext()methoddefinesyourplain-textview:

publicfunctionbuild()

{

return$this->view('emails.reminder')

->text('emails.reminder_plain');

}

Page 578: Laravel: Up and Running: A Framework for Building Modern PHP

MethodsAvailableinbuild()Hereareafewofthemethodsavailabletoyoutocustomizeyourmessageinthebuild()methodofyourmailable:

from($address,$name=null)

Setsthe“from”nameandaddress—representstheauthor

subject($subject)

Setstheemailsubject

attach($pathToFile,array$options=[])

Attachesafile;validoptionsaremimeforMIMEtypeandasfordisplayname

attachData($data,$name,array$options=[])

Attachesafilefromarawstring;sameoptionsasattach()

priority($priority)

Settheemail’spriority,where1isthehighestand5isthelowest

Finally,ifyouwanttoperformanymanualmodificationsontheunderlyingSwiftmessage,youcandothatusingwithSwiftMessage(),asshowninExample15-7.

Example15-7.ModifyingtheunderlyingSwiftMessageobjectpublicfunctionbuild()

{

return$this->subject('Howdy!')

->withSwiftMessage(function($swift){

$swift->setReplyTo('[email protected]');

})

->view('emails.howdy');

}

Page 579: Laravel: Up and Running: A Framework for Building Modern PHP

AttachmentsandInlineImagesExample15-8showstwooptionsforhowtoattachfilesorrawdatatoyouremail.

Example15-8.Attachingfilesordatatomailables//Attachafileusingthelocalfilename

publicfunctionbuild()

{

return$this->subject('Yourwhitepaperdownload')

->attach(storage_path('pdfs/whitepaper.pdf'),[

'mime'=>'application/pdf',//Optional

'as'=>'whitepaper-barasa.pdf'//Optional

])

->view('emails.whitepaper');

}

//Attachafilepassingtherawdata

publicfunctionbuild()

{

return$this->subject('Yourwhitepaperdownload')

->attachData(

file_get_contents(storage_path('pdfs/whitepaper.pdf')),

'whitepaper-barasa.pdf',

[

'mime'=>'application/pdf'//Optional

]

)

->view('emails.whitepaper');

}

AndyoucanseehowtoembedimagesdirectlyintoyouremailinExample15-9.

Example15-9.Inliningimages<!--emails/image.blade.php--!>

Hereisanimage:

<imgsrc="{{$message->embed(storage_path('embed.jpg'))}}">

Or,thesameimageembeddingthedata:

<imgsrc="{{$message->embedData(

file_get_contents(storage_path('embed.jpg')),'embed.jpg'

)}}">

Page 580: Laravel: Up and Running: A Framework for Building Modern PHP

QueuesSendingemailisatime-consumingtaskthatcancauseapplicationstoslowdown,soit’scommontomovesendingemailtoabackgroundqueue.It’ssocommon,infact,thatLaravelhasasetofbuilt-intoolstomakeiteasiertoqueueyourmessageswithoutwritingqueuejobsforeachemail.

Page 581: Laravel: Up and Running: A Framework for Building Modern PHP
Page 582: Laravel: Up and Running: A Framework for Building Modern PHP

CONFIGURINGQUEUESEverythingwe’llcoverhererequiresyourqueuestobeconfiguredcorrectly.TakealookatChapter16tolearnmoreabouthowqueuesworkandhowtogetthemrunninginyourapplication.

queue()Toqueueamailobjectinsteadofsendingitimmediately,simplypassyourmailableobjecttoMail::queue()insteadofMail::send():

Mail::queue(newAssignment($trainer,$trainee));

later()Mail::later()worksthesameasMail::queue(),butitallowsyoutoaddadelay—eitherinminutes,orataspecifictimebypassinganinstanceofDateTimeorCarbon—towhentheemailwillbepulledfromthequeueandsent:

$when=Carbon::now()->addMinutes(30);

Mail::later($when,newAssignment($trainer,$trainee));

SpecifyingthequeueorconnectionForbothqueue()andlater(),ifyou’dliketospecifywhichqueueorqueueconnectionyourmailisaddedto,usetheonConnection()andonQueue()methodsonyourmailableobject:

$message=(newAssignment($trainer,$trainee))

->onConnection('sqs')

->onQueue('emails');

Mail::to($user)->queue($message);

Page 583: Laravel: Up and Running: A Framework for Building Modern PHP

LocalDevelopmentThisisallwellandgoodforsendingmailinyourproductionenvironments.Buthowdoyoutestthisallout?Therearethreeprimarytoolsyou’llwanttoconsider:Laravel’slogdriver,aSoftwareasaService(SaaS)appnamedMailtrap,andthe“universalto”configurationoption.

ThelogdriverLaravelprovidesalogdriverthatlogseveryemailyoutrytosendtoyourlocallaravel.logfile(whichis,bydefault,instorage/logs).

Ifyouwanttousethis,edit.envandsetMAIL_DRIVERtolog.Nowopenuportailstorage/logs/laravel.logandsendanemailfromyourapp.You’llseesomethinglikethis:

Message-ID:<04ee2e97289c68f0c9191f4b04fc0de1@localhost>

Date:Tue,17May201602:52:46+0000

Subject:Welcometoourapp!

From:MattStauffer<[email protected]>

To:[email protected]

MIME-Version:1.0

Content-Type:text/html;charset=utf-8

Content-Transfer-Encoding:quoted-printable

Welcometoourapp!

Mailtrap.ioMailtrapisaserviceforcapturingandinspectingemailsindevelopmentenvironments.YousendyourmailtotheMailtrapserversviaSMTP,butinsteadofsendingthoseemailsofftotheintendedrecipients,Mailtrapcapturesthemallandprovidesyouwithaweb-basedemailclientforinspectingthem,regardlessofwhichemailaddressisinthetofield.

TosetupMailtrap,signupforafreeMailtrapaccountandvisitthebasedashboardforyourdemo.CopyyourusernameandpasswordfromtheSMTPcolumn.

Nowedityourapp’s.envfileandsetthefollowingvaluesinthemailsection:

MAIL_DRIVER=smtp

MAIL_HOST=mailtrap.io

MAIL_PORT=2525

MAIL_USERNAME=your_username_from_mailtrap_here

MAIL_PASSWORD=your_password_from_mailtrap_here

MAIL_ENCRYPTION=null

Now,anyemailyousendfromyourappwillshowupinyourMailtrapinbox.

UniversaltoIfyou’dliketoinspecttheemailsinyourpreferredclient,youcanoverridethetofieldoneachmessagewiththe“universalto”configurationsetting.Tosetthisup,adda“to”keytoyourconfig/mail.phpfilethatlookssomethinglikethis:

Page 584: Laravel: Up and Running: A Framework for Building Modern PHP

'to'=>[

'address'=>'[email protected]',

'name'=>'MattTestingMyApplication'

],

Notethatyou’llneedtoactuallysetuparealemaildriverwithsomethinglikeMailgunorSendmailinordertousethis.

Page 585: Laravel: Up and Running: A Framework for Building Modern PHP

NotificationsMostofthemailthat’ssentfromwebappsreallyhasthepurposeofnotifyingusersthataparticularactionhashappenedorneedstohappen.Asusers’communicationpreferencesgrowmoreandmorediverse,wegatherevermore—andmoredisparate—packagestocommunicateviaSlack,SMS,andothermeans.

Laravel5.3introducedanewconceptinLaravelcalled,fittingly,notifications.Justlikemailables,anotificationisaPHPclassthatrepresentsasinglecommunicationthatyoumightwanttosendtoyourusers.Fornow,let’simaginewe’renotifyingusersofourphysicaltrainingappthattheyhaveanewworkoutavailableonourapp.

Eachclassrepresentsalloftheinformationnecessarytosendnotificationstoyourusersusingoneormanynotificationchannels.Asinglenotificationcouldsendanemail,sendanSMSviaNexmo,sendaWebSocketsping,addarecordtoadatabase,sendamessagetoaSlackchannel,andmuchmore.

So,let’screateournotification:

phpartisanmake:notificationWorkoutAvailable

Example15-10showswhatthatgivesus.

Example15-10.Anautogeneratednotificationclass<?php

namespaceApp\Notifications;

useIlluminate\Bus\Queueable;

useIlluminate\Notifications\Notification;

useIlluminate\Contracts\Queue\ShouldQueue;

useIlluminate\Notifications\Messages\MailMessage;

classWorkoutAvailableextendsNotification

{

useQueueable;

/**

*Createanewnotificationinstance.

*

*@returnvoid

*/

publicfunction__construct()

{

//

}

/**

*Getthenotification'sdeliverychannels.

*

*@parammixed$notifiable

*@returnarray

*/

publicfunctionvia($notifiable)

{

return['mail'];

}

/**

*Getthemailrepresentationofthenotification.

Page 586: Laravel: Up and Running: A Framework for Building Modern PHP

*

*@parammixed$notifiable

*@return\Illuminate\Notifications\Messages\MailMessage

*/

publicfunctiontoMail($notifiable)

{

return(newMailMessage)

->line('Theintroductiontothenotification.')

->action('NotificationAction','https://laravel.com')

->line('Thankyouforusingourapplication!');

}

/**

*Getthearrayrepresentationofthenotification.

*

*@parammixed$notifiable

*@returnarray

*/

publicfunctiontoArray($notifiable)

{

return[

//

];

}

}

Wecanlearnafewthingshere.First,we’regoingtopassinrelevantdatatotheconstructor.Second,there’savia()methodthatallowsustodefine,foragivenuser,whichnotificationchannelstouse($notifiablerepresentswhateverentitiesyouwanttonotifyinyoursystem;formostapps,it’llbeauser,butthat’snotalwaysthecase).Andthird,thereareindividualmethodsforeachnotificationchannelthatallowyoutospecificallydefinehowtosendoneofthesenotificationsthroughthatchannel.

Page 587: Laravel: Up and Running: A Framework for Building Modern PHP
Page 588: Laravel: Up and Running: A Framework for Building Modern PHP

WHENWOULDA$NOTIFIABLENOTBEAUSER?Whilethemostcommonnotificationtargetswillbeusers,it’spossibleyoumaywanttonotifysomethingelse.Thismaysimplybebecauseyourapplicationhasmultipleusertypes—so,youmightwanttobeabletonotifyboth“trainers”and“trainees.”Butyoualsomightfindyourselfwantingtonotifya“group,”a“company,”ora“server.”

So,let’smodifythisclassforourWorkoutAvailableexample.TakealookatExample15-11.

Example15-11.OurWorkoutAvailablenotificationclass...

classWorkoutAvailableextendsNotification

{

useQueueable;

public$workout;

publicfunction__construct($workout)

{

$this->workout=$workout;

}

publicfunctionvia($notifiable)

{

//Thismethoddoesn'texistontheUser...we'regoingtomakeitup

return$notifiable->preferredNotificationChannels();

}

publicfunctiontoMail($notifiable)

{

return(newMailMessage)

->line('Youhaveanewworkoutavailable!')

->action('Checkitoutnow',route('workout',[$this->workout]))

->line('Thankyoufortrainingwithus!');

}

publicfunctiontoArray($notifiable)

{

return[];

}

}

Page 589: Laravel: Up and Running: A Framework for Building Modern PHP

Definingthevia()MethodforYourNotifiablesAsyoucanseeinExample15-11,we’resomehowresponsiblefordeciding,foreachnotificationandeachnotifiable,whichnotificationchannelswe’regoingtouse.

YoucouldjustsendeverythingasmailorjustsendeverythingasSMS(Example15-12).

Example15-12.Simplestpossiblevia()methodpublicfunctionvia($notifiable)

{

return'nexmo';

}

YoucouldalsoleteachuserchoosetheironepreferredmethodandsavethatontheUseritself(Example15-13).

Example15-13.Customizingthevia()methodperuserpublicfunctionvia($notifiable)

{

return$notifiable->preferred_notification_channel;

}

Or,asweimaginedinExample15-11,youcouldcreateamethodoneachnotifiablethatallowsforsomecomplexnotificationlogic.Forexample,youcouldnotifytheuserovercertainchannelsduringworkhoursandotherchannelsintheevening.Whatisimportantisthatvia()isaPHPclassmethod,soyoucandowhatevercomplexlogicyouwantthere.

Page 590: Laravel: Up and Running: A Framework for Building Modern PHP

SendingNotificationsTherearetwowaystosendanotification:usingtheNotificationfacade,oraddingtheNotifiabletraittoanEloquentclass(likelyyourUserclass).

SendingnotificationsusingtheNotifiabletraitAnymodelthatimportstheLaravel\Notifications\Notifiabletrait(whichtheApp\Userclassdoesbydefault)hasanotify()methodthatcanbepassedanotification,whichwilllooklikeExample15-14.

Example15-14.SendinganotificationusingtheNotifiabletraituseApp\Notifications\WorkoutAvailable;

...

$user->notify(newWorkoutAvailable($workout));

SendingnotificationswiththeNotificationfacadeTheNotificationfacadeistheclumsierofthetwomethods,sinceyouhavetopassboththenotifiableandthenotification.However,it’shelpfulbecauseyoucanchoosetopassmorethanonenotifiableinatthesametime,likeyoucanseeinExample15-15.

Example15-15.SendingnotificationsusingtheNotificationfacadeuseApp\Notifications\WorkoutAvailable;

...

Notification::send($users,newWorkoutAvailable($workout));

Page 591: Laravel: Up and Running: A Framework for Building Modern PHP

QueueingNotificationsMostofthenotificationdriversneedtosendHTTPrequeststosendtheirnotifications,whichcouldslowdownyouruserexperience,soyouprobablywanttoqueueyournotifications.AllnotificationsimporttheQueuabletraitbydefault,soallyouneedtodoisaddimplementsShouldQueuetoyournotificationandLaravelwillinstantlymoveittoaqueue.

Aswithanyotherqueuedfeatures,you’llneedtomakesureyouhaveyourqueuesettingsconfiguredcorrectlyandaqueueworkerrunning.

Ifyou’dliketodelaythedeliveryofyournotifcation,youcanrunthedelay()methodonthenotification:

$delayUntil=Carbon::now()->addMinutes(15);

$user->notify((newWorkoutAvailable($workout))->delay($delayUntil));

Page 592: Laravel: Up and Running: A Framework for Building Modern PHP

Out-of-the-BoxNotificationTypesOutofthebox,Laravelcomeswithnotificationdriversforemail,database,broadcast,NexmoSMS,andSlack.I’llcovereachbriefly,butI’drecommendreferringtothedocsformorethoroughintroductionstoeach.

It’salsoeasytocreateyourownnotificationdrivers,anddozensofpeoplealreadyhave;youcanfindthematLaravelNotificationChannelswebsite.

EmailnotificationsLet’stakealookathowtheemailfromourearlierexample,Example15-11,isbuilt:

publicfunctiontoMail($notifiable)

{

return(newMailMessage)

->line('Youhaveanewworkoutavailable!')

->action('Checkitoutnow',route('workout',[$this->workout]))

->line('Thankyoufortrainingwithus!');

}

TheresultisshowninFigure15-1.Theemailnotificationsystemputsyourapplication’snameintheheaderoftheemail;youcancustomizethatappnameinthenamekeyofconfig/app.php.

Thisemailisautomaticallysenttotheemailpropertyonthenotifiable,butyoucancustomizethisbehaviorbyaddingamethodtoyournotifiableclassnamedrouteNotificationForMail()thatreturnstheemailaddressyou’dlikeemailnotificationssentto.

Theemail’ssubjectissetbyparsingthenotificationclassnameandconvertingittowords.So,ourWorkoutAvailablenotificationwouldhavethedefaultsubjectofWorkoutAvailable.Wecanalsocustomizethisbychainingthesubject()methodonourMailMessageinthetoMail()method.

Ifyouwanttomodifythetemplates,publishthemandedittoyourheart’scontent:

phpartisanvendor:publish--tag=laravel-notifications

Youcanalsochangethestyleofthedefaulttemplatetobean“error”message,whichusesabitofdifferentlanguageandchangestheprimarybuttoncolortored.Justaddacalltotheerror()methodtoyourMailMessagecallchaininthetoMail()method.

Page 593: Laravel: Up and Running: A Framework for Building Modern PHP

Figure15-1.Anemailsentwiththedefaultnotificationtemplate

DatabasenotificationsYoucansendnotificationstoadatabasetableusingthedatabasenotificationchannel.First,createyourtablewithphpartisannotifications:table.Next,createatoDatabase()methodonyournotificationandreturnanarrayofdatathere.ThisdatawillbeencodedasJSONandstoredinthedatabasetable’sdatacolumn.

TheNotifiabletraitaddsanotificationsrelationshiptoanymodelit’simportedin,allowingyoutoeasilyaccessrecordsinthenotificationstable.Soifyou’reusingdatabasenotifications,youcouldsosomethinglikethis:

User::first()->notifications->each(function($notification){

//dosomething

});

Thedatabasenotificationchannelalsohastheconceptofwhetherornotanotificationis“read.”Youcanscopetoonlythe“unread”notifications:

User::first()->unreadNotifications->each(function($notification){

//dosomething

Page 594: Laravel: Up and Running: A Framework for Building Modern PHP

});

Andyoucanmarkoneorallnotificationsasread:

//Individual

User::first()->notifications->each(function($notification){

if($condition){

$notification->markAsRead();

}

});

//All

User::first()->unreadNotifications->markAsRead();

BroadcastnotificationsThebroadcastchannelsendsnotificationsoutusingLaravel’seventbroadcastingfeatures(Echo).

CreateatoBroadcast()methodonyournotificationandreturnarrayofdata,andifyourappiscorrectlyconfiguredforeventbroadcasting,thatdatawillbebroadcastonaprivatechannelnamed{notifiable}.{id}.The{id}willbetheIDofthenotifiable,and{notifiable}willbethenotifiable’sfullyqualifiedclassname,withtheslashesreplacedbyperiods—forexample,theprivatechannelfortheApp\UserwiththeIDof1willbeApp.User.1.

SMSnotificationsSMSnotificationsaresentviaNexmo,soifyouwanttosendSMSnotifications,signupforaNexmoaccountandfollowtheinstructionsinthedocs.Likewiththeotherchannels,you’llbesettingupatoNexmo()methodandcustomizingtheSMSmessagethere.

SlacknotificationsTheslacknotificationchannelallowsyoutocustomizetheappearanceofyournotificationsandevenattachfilestoyournotifications.Likewiththeotherchannels,you’llsetupatoSlack()methodandcustomizethemessagethere.

Page 595: Laravel: Up and Running: A Framework for Building Modern PHP

TestingLet’stakealookathowtotestmailandnotifications.

Page 596: Laravel: Up and Running: A Framework for Building Modern PHP

MailTherearetwooptionsfortestingmailinLaravel.Ifyou’reusingthetraditionalmailsyntax,I’drecommendusingatoolcalledMailThief,whichAdamWathanwroteforTighten.OnceyoubringMailThiefintoyourapplicationwithComposer,youcanuseMailThief::hijack()inyourteststomakeMailThiefcaptureanycallstotheMailfacadeoranymailerclasses.

MailThiefthenmakesitpossibletomakeassertionsagainstthesenders,recipients,CCandBCCvalues,andevencontentandattachmentsofyourmail.TakealookattheGitHubrepotolearnmore,orbringitintoyourapp:

composerrequiretightenco/mailthief--dev

Ifyouareusingmailables,there’sasimplesyntaxforwritingassertionsagainstyoursentmail(Example15-16).

Example15-16.Assertingagainstmailablespublicfunctiontest_signup_triggers_welcome_email()

{

...

Mail::assertSent(WelcomeEmail::class,function($e){

return$e->subject=='Welcome!';

});

//YoucanalsouseassertSentTo()toexplicitlytesttherecipients

}

Page 597: Laravel: Up and Running: A Framework for Building Modern PHP

NotificationsLaravelprovidesabuilt-insetofassertionsfortestingyournotifications.Example15-17demonstrates.

Example15-17.Assertingnotificationsweresentpublicfunctiontest_new_signups_triggers_admin_notification()

{

...

Notification::assertSentTo($user,NewUsersSignedup::class,

function($n,$channels){

return$n->user->email=='[email protected]'

&&$channels==['mail'];

});

//YoucanalsouseassertNotSentTo()

}

Page 598: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRLaravel’smailandnotificationfeaturesprovidesimple,consistentinterfacestoavarietyofmessagingsystems.Laravel’smailsystemuses“mailables,”PHPclassesthatrepresentemails,toprovideaconsistentsyntaxtodifferentmaildrivers.Thenotificationsystemmakesiteasytobuildasinglenotificationthatcanbedeliveredinmanydifferentmedia—fromemailstoSMSmessagestophysicalpostcards.

Page 599: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter16.Queues,Jobs,Events,Broadcasting,andtheScheduler

Sofarwe’vecoveredsomeofthemostcommonstructuresthatpowerwebapplications:databases,mail,filesystems,andmore.Eachofthesearecommonacrossamajorityofapplicationsandframeworks.

Laravelalsoprovidesfacilitiesforsomelesscommonarchitecturepatternsandapplicationstructures.Inthischapterwe’llcoverLaravel’stoolsforimplementingqueues,queuedjobs,events,andWebSocketeventpublishing.We’llalsocoverLaravel’sscheduler,whichmakescronathingofthepast.

Page 600: Laravel: Up and Running: A Framework for Building Modern PHP

QueuesTounderstandwhataqueueis,justthinkabouttheideaof“queueingup”inalineatthebank.Eveniftherearemultiplelines—queues—onlyonepersonisbeingservedatatimefromeachqueue,andeachpersonwilleventuallyreachthefrontandbeserved.Insomebanks,it’sastrictfirst-in-first-outsortofpolicy,butinotherbanks,there’snotanexactguaranteethatsomeonewon’tcutaheadofyouinlineatsomepoint.Essentially,someonecangetaddedtothequeue,beremovedfromthequeueprematurely,orbesuccessfully“processed”andthenremoved.Someonemightevenhitthefrontofthequeue,notbeabletobeservedcorrectly,returntothequeueforatime,andthenbeprocessedagain.

Queuesinprogrammingareverysimilar.Yourapplicationaddsa“job”toaqueue,whichisachunkofcodethattellstheapplicationhowtoperformaparticularbehavior.Thensomeotherseparateapplicationstructure,usuallya“queueworker,”takestheresponsibilityforpullingjobsoffofthequeueoneatatimeandperformingtheappropriatebehavior.Queueworkerscandeletethejobs,returnthemtothequeuewithadelay,ormarkthemassuccessfullyprocessed.

LaravelmakesiteasytoserveyourqueuesusingRedis,beanstalkd,Amazon’sSQS(SimpleQueueService),oradatabasetable.Youcanalsochoosethesyncdrivertohavethejobsrunrightinyourapplicationwithoutactuallybeingqueued,orthenulldriverforjobstojustbediscarded;thesetwoareusuallyusedinlocaldevelopmentortestingenvironments.

Page 601: Laravel: Up and Running: A Framework for Building Modern PHP

WhyQueues?Queuesmakeiteasytoremoveacostlyorslowprocessfromanysynchronouscall.Themostcommonexampleissendingmail—doingsocanbeslow,andyoudon’twantyouruserstohavetowaitformailtosendinresponsetotheiractions.Instead,triggera“sendmail”queuedjobandlettheusersgetonwiththeirday.Sometimesyoumaynotjustwanttosaveyouruserstime,butyoumighthaveaprocesslikeacronjoborawebhookthathasalotofworktoprocess;ratherthanlettingitallrunatonce(andpotentiallytimeout),youmaychoosetoqueueitsindividualpiecesandletthequeueworkerprocessthemoneatatime.

Additionally,ifyouhavesomeheavyprocessingthat’smorethanyourservercanhandle,youcanspinupmorethanonequeueworkertoworkthroughyourqueuefasterthanyournormalapplicationservercouldonitsown.

Page 602: Laravel: Up and Running: A Framework for Building Modern PHP

BasicQueueConfigurationLikemanyotherLaravelfeaturesthatabstractmultipleproviders,queueshavetheirowndedicatedconfigfile(config/queue.php)thatallowsyoutosetupmultipledriversanddefinewhichwillbethedefault.Thisisalsowhereyou’llstoreyourSQS,Redis,orbeanstalkdauthenticationinformation.

Page 603: Laravel: Up and Running: A Framework for Building Modern PHP
Page 604: Laravel: Up and Running: A Framework for Building Modern PHP

SIMPLEBEANSTALKDQUEUESONLARAVELFORGEWehaven’tcoveredLaravelForgeinmuchdepth,butit’sahostingserviceprovidedbyTaylorOtwell,thecreatorofLaravel.Everyserveryoucreatehasbeanstalkdconfiguredautomatically,soifyouvisitanysite’sForgeconsole,youcanjustgototheQueueWorkerstabandhitStartWorkerandyou’rereadytousebeanstalkdasyourqueuedriver;youcanleaveallthedefaultsettings,andnootherworkisnecessary.

Page 605: Laravel: Up and Running: A Framework for Building Modern PHP

QueuedJobsRememberourbankanalogy?Eachpersoninthebank“queue”(line)is,inprogrammingterms,ajob.Thisjobcouldbeshapedanyway;itcouldjustbeastring,oranarray,oranobject.InLaravel,it’sacollectionofinformationcontainingthejobname,thedatapayload,thenumberofattemptsthathavebeenmadesofartoprocessthisjob,andsomeothersimplemetadata.

Butyoudon’tneedtoworryaboutthatinyourinteractionswithLaravel.LaravelprovidesastructurecalledaJob,whichisintendedtoencapsulateasingletask—abehaviorthatyourapplicationcanbecommandedtodo—andallowittobeaddedtoandpulledfromaqueue.TherearealsosimplehelperstomakeiteasytoqueueArtisancommandsandmail.

Let’sstartwithanexamplewhere,everytimeauserchangeshisplanwithyourSaaSapp,youwanttorerunsomecalculationsaboutyouroverallprofit.

CreatingajobAsalways,there’sanArtisancommandforthat:

phpartisanmake:jobCrunchReports

TakealookatExample16-1toseewhatyou’llget.

Example16-1.ThedefaulttemplateforjobsinLaravel<?php

useIlluminate\Bus\Queueable;

useIlluminate\Queue\SerializesModels;

useIlluminate\Queue\InteractsWithQueue;

useIlluminate\Contracts\Queue\ShouldQueue;

classCrunchReportsimplementsShouldQueue

{

useInteractsWithQueue,Queueable,SerializesModels;

/**

*Createanewjobinstance.

*

*@returnvoid

*/

publicfunction__construct()

{

//

}

/**

*Executethejob.

*

*@returnvoid

*/

publicfunctionhandle()

{

//

}

}

Asyoucansee,thistemplateimportstheQueueable,InteractsWithQueue,and

Page 606: Laravel: Up and Running: A Framework for Building Modern PHP

SerializesModelstraits,andimplementstheShouldQueueinterface.PriortoLaravel5.3,someofthisfunctionalitycameinthroughtheparentApp\Jobsclass.

Wealsogettwomethodsfromthistemplate:theconstructor,whichyou’llwanttousetoattachdatatothejob,andthehandle()method,whichiswherethejob’slogicshouldreside(andisalsothemethodsignatureyou’llusetoinjectdependencies).

Thetraitsandinterfaceprovidetheclasswiththeabilitytobeaddedto,andinteractwith,thequeue.QueueableallowsyoutospecifyhowLaravelshouldpushthisjobtothequeue;InteractsWithQueueallowseachjob,whilebeinghandled,tocontrolitsrelationshipwiththequeue,includingdeletingorrequeueingitself;andSerializesModelsgivesthejobtheabilitytoserializeanddeserializeEloquentmodels.

Page 607: Laravel: Up and Running: A Framework for Building Modern PHP
Page 608: Laravel: Up and Running: A Framework for Building Modern PHP

SERIALIZINGMODELSTheSerializesModelstraitgivesthejobstheabilitytoserializeinjectedmodelssothatyourjob’shandle()methodwillhaveaccesstothem.However,becauseit’stoodifficulttoreliablyserializeanentireEloquentobject,thetraitensuresthatjusttheprimarykeysofanyattachedEloquentobjectsareserializedwhenthejobispushedontothequeue.Whenthejobisdeserializedandhandled,thetraitpullsthoseEloquentmodelsfreshfromthedatabasebytheirprimarykey.Thismeansthatwhenyourjobrunsitwillbepullingafreshinstanceofthismodel,notwhateverstateitwasinwhenyouqueuedthejob.

Let’sfilloutthemethodsforoursampleclass,asinExample16-2.

Example16-2.Anexamplejob...

useApp\ReportGenerator;

useIlluminate\Log\WriterasLogger;

classCrunchReportsimplementsShouldQueue

{

useInteractsWithQueue,SerializesModels;

protected$user;

publicfunction__construct($user)

{

$this->user=$user;

}

publicfunctionhandle(ReportGenerator$generator,Logger$logger)

{

$generator->generateReportsForUser($this->user);

$logger->info('Generatedreports.');

}

}

We’reexpectingtheUserinstancetobeinjectedwhenwecreatethejob,andthenwhenit’shandledwe’retypehintingaReportGeneratorclass(whichwepresumablywrote)andaLogger(whichLaravelprovides).Laravelwillreadbothtypehintsandinjectthosedependenciesautomatically.

PushingajobontoaqueueTherearetwoprimarywaysyoucanpushajobontoaqueue:theglobaldispatch()helperandthemethodsprovidedbytheDispatchesJobstrait,whichisimportedbydefaultineverycontroller.

Witheach,createaninstanceofyourjob,attachanynecessarydatabypassingittotheconstructor,andpassittothedispatch()method(seeExample16-3).

Example16-3.Dispatchingjobs//Inacontroller

publicfunctionindex()

{

$user=auth()->user();

$this->dispatch(new\App\Jobs\CrunchReports($user));

}

Page 609: Laravel: Up and Running: A Framework for Building Modern PHP

//Elsewhere

dispatch(new\App\Jobs\CrunchReports($user));

Therearethreesettingsyoucancontrolinordertocustomizeexactlyhowyoudispatchajob:theconnection,thequeue,andthedelay.

CustomizingtheconnectionIfyoueverhavemultiplequeueconnectionsinplaceatonce,youcancustomizetheconnectionbyrunningonConnection()onyourinstantiatedjob:

dispatch((newDoThingJob)->onConnection('redis'));

CustomizingthequeueWithinqueueservers,youcanspecifywhichnamedqueueyou’repushingajobonto.Forexample,youmaydifferentiateyourqueuesbasedontheirimportance,namingonelowandonehigh.

Youcancustomizewhichqueueyou’repushingajobontowiththeonQueue()method:

dispatch((newDoThingJob)->onQueue('high'));

CustomizingthedelayYoucancustomizetheamountoftimeyourqueueworkersshouldwaitbeforeprocessingajobwiththedelay()method,whichacceptsanintegerrepresentingthenumberofsecondstodelayajob:

//Delaysoneminutebeforereleasingthejobtoqueueworkers

dispatch((newDoThingJob)->delay(60));

NotethatAmazonSQSdoesn’tallowdelayslongerthan15minutes.

Page 610: Laravel: Up and Running: A Framework for Building Modern PHP

RunningaQueueWorkerSowhatisaqueueworker,andhowdoesitwork?InLaravel,it’sanArtisancommandthatstaysrunningforever(untilit’sstoppedmanually)andtakestheresponsibilityforpullingdownjobsfromyourqueueandrunningthem:

phpartisanqueue:work

Thiscommandstartsadaemon“listening”toyourqueue;everytimetherearejobsonthequeue,itwillpulldownthefirstjob,handleit,deleteit,andmoveontothenext.Ifatanypointtherearenojobs,it“sleeps”foraconfigurableamountoftimebeforecheckingagaintoseeifthereareanymorejobs.

Youcandefinehowmanysecondsajobshouldbeabletorunbeforethequeuelistenerstopsit(--timeout),howmanysecondsthelistenershould“sleep”whentherearenojobsleft(--sleep),howmanytrieseachjobshouldbeallowedbeforebeingdeleted(--tries),whichconnectiontheworkershouldlistento(thefirstparameterafterqueue:work),andwhichqueuesitshouldlistento(--queue=):

phpartisanqueue:workredis--timeout=60--sleep=15--tries=3

--queue=high,medium

Youcanalsoprocessjustasinglejobwithphpartisanqueue:work.

Page 611: Laravel: Up and Running: A Framework for Building Modern PHP

HandlingErrorsSo,whathappenswhensomethinggoeswrongwithyourjobwhenit’sinthemiddleofprocessing?

ExceptionsinhandlingIfanexceptionisthrown,thequeuelistenerwillreleasethatjobbackontothequeue.Thatjobwillberereleasedtobeprocessedagainandagainuntilitisabletofinishsuccessfullyoruntilithasbeenattemptedthemaximumnumberoftimesallowedbyyourqueuelistener.

LimitingthenumberoftriesThemaximumnumberoftriesisdefinedbythe--triesswitchpassedtothequeue:listenorqueue:workArtisancommands.

Page 612: Laravel: Up and Running: A Framework for Building Modern PHP
Page 613: Laravel: Up and Running: A Framework for Building Modern PHP

THEDANGEROFINFINITERETRIESIfyoudon’tset--tries,orifyousetitto0,thequeuelistenerwillallowforinfiniteretries.Thatmeansifthereareanycircumstancesinwhichajobcouldjustneverbesatisfied—forexample,ifitreliesonatweetthathassincebeendeleted—yourappwillslowlycrawltoahaltasitforeverretriesuncompletablejobs.

ThedocumentationandLaravelForgebothshow3asthedefaultstartingpointforthemaximumnumberofretries.So,incaseofconfusion,startthereandadjust:

phpartisanqueue:listen--tries=3

Ifatanypointyou’dliketocheckhowmanytimesajobhasbeenattemptedalready,usetheattempts()methodonthejobitself,asinExample16-4.

Example16-4.Checkinghowmanytimesajobhasalreadybeentriedpublicfunctionhandle()

{

...

if($this->attempts()>3){

//

}

}

HandlingfailedjobsOnceajobhasexceededitsallowablenumberofretries,it’sconsidereda“failed”job.Beforeyoudoanythingelse—evenifallyouwanttodoislimitthenumberoftimesajobcanbetried—you’llneedtocreatethe“failedjobs”databasetable.

There’sanArtisancommandtocreatethemigration(andyou’llthenwanttomigrate):

phpartisanqueue:failed-table

phpartisanmigrate

Anyjobthathassurpasseditsmaximumnumberofallowedattemptswillbedumpedthere.Buttherearequiteafewthingsyoucandowithyourfailedjobs.

First,youcandefineafailed()methodonthejobitself,whichwillrunwhenthatjobfails(seeExample16-5).

Example16-5.Definingamethodtorunwhenajobfails...

classCrunchReportsimplementsShouldQueue

{

...

publicfunctionfailed()

{

//Dowhateveryouwant

}

}

Next,youcanregisteraglobalhandlerforfailedjobs.Somewhereintheapplication’s

Page 614: Laravel: Up and Running: A Framework for Building Modern PHP

bootstrap—ifyoudon’tknowwheretoputit,justputitintheboot()methodofAppServiceProvider—placethecodeinExample16-6codetodefinealistener.

Example16-6.Registeringaglobalhandlertohandlefailedjobs//Someserviceprovider

useIlluminate\Support\Facades\Queue;

...

publicfunctionboot()

{

Queue::failing(function($connection,$job,$data){

//Dowhateveryouwant

});

}

ThereisalsoasuiteofArtisantoolsforinteractingwiththefailedjobstable.

queue:failedshowsyoualistofyourfailedjobs:

phpartisanqueue:failed

Thelistwilllooksomethinglikethis:

+----+------------+---------+----------------------+---------------------+

|ID|Connection|Queue|Class|FailedAt|

+----+------------+---------+----------------------+---------------------+

|9|database|default|App/Jobs/AlwaysFails|2016-01-2603:42:55|

+----+------------+---------+----------------------+---------------------+

Fromthere,youcangrabtheIDofanyindividualfailedjobandretryitwithqueue:retry:

phpartisanqueue:retry9

Ifyou’dratherretryallofthejobs,passallinsteadofanID:

phpartisanqueue:retryall

Youcandeleteanindividualfailedjobwithqueue:forget:

phpartisanqueue:forget5

Andyoucandeleteallofyourfailedjobswithqueue:flush:

phpartisanqueue:flush

Page 615: Laravel: Up and Running: A Framework for Building Modern PHP

ControllingtheQueueSometimes,fromwithinthehandlingofajob,you’llwanttoaddconditionsthatwillpotentiallyeitherreleasethejobtoberestartedlaterordeletethejobforever.

Toreleaseajobbackintothequeue,usetherelease()command,asinExample16-7.

Example16-7.Releasingajobbackontothequeuepublicfunctionhandle()

{

...

if(condition){

$this->release($numberOfSecondsToDelayBeforeRetrying);

}

}

Ifyouwanttodeleteajobduringitshandling,youcanjustreturnatanypoint,asseeninExample16-8;that’sthesignaltothequeuethatthejobwashandledappropriatelyandshouldnotbereturnedtothequeue.

Example16-8.Deletingajobpublicfunctionhandle()

{

...

if($jobShouldBeDeleted){

return;

}

}

Page 616: Laravel: Up and Running: A Framework for Building Modern PHP

QueuesSupportingOtherFunctionsTheprimaryuseforqueuesistopushjobsonto,butyoucanalsoqueuemailusingtheMail::queuefunctionality.Youcanlearnmoreaboutthisin“queue()”.YoucanalsoqueueArtisancommands,whichwecoveredinChapter7.

Page 617: Laravel: Up and Running: A Framework for Building Modern PHP

EventsWithjobs,thecallingcodeinformstheapplicationthatitshoulddosomething:CrunchReports,orNotifyAdminOfNewSignup.

Withanevent,thecallingcodeinsteadinformstheapplicationthatsomethinghappened:UserSubscribed,orUserSignedUp,orContactWasAdded.Eventsarenotificationsthatsomethinghastakenplace.

Someoftheseeventsmaybe“fired”bytheframeworkitself.Forexample,Eloquentmodelsfireeventswhentheyaresaved,orcreated,ordeleted.Butsomeeventsarealsomanuallytriggeredbytheapplication’scode.

Aneventbeingfireddoesn’tdoanythingonitsown.However,youcanbindeventlisteners,whosesolepurposeistolistenforthebroadcastingofspecificeventsandtoactinresponse.Anyeventcanhaveanywherefromzerotomanyeventlisteners.

Laravel’seventsarestructuredliketheobserver,or“pub/sub,”pattern.Manyeventsarefiredoutintotheapplication;somemayneverbelistenedfor,andothersmayhaveadozenlisteners.Theeventsdon’tknoworcare.

Page 618: Laravel: Up and Running: A Framework for Building Modern PHP

FiringanEventTherearethreewaystofireanevent.YoucanusetheEventfacade,injecttheDispatcher,orusetheevent()globalhelper:

Event::fire(newUserSubscribed($user,$plan));

//or

$dispatcher=app(Illuminate\Contracts\Events\Dispatcher);

$dispatcher->fire(newUserSubscribed($user,$plan));

//or

event(newUserSubscribed($user,$plan));

Ifindoubt,I’drecommendusingtheglobalhelperfunction.

Tocreateaneventtofire,usethemake:eventArtisancommand:

phpartisanmake:eventUserSubscribed

That’llmakeafilethatlookssomethinglikeExample16-9.

Example16-9.ThedefaulttemplateforaLaravelevent<?php

namespaceApp\Events;

useIlluminate\Broadcasting\Channel;

useIlluminate\Queue\SerializesModels;

useIlluminate\Broadcasting\PrivateChannel;

useIlluminate\Broadcasting\PresenceChannel;

useIlluminate\Broadcasting\InteractsWithSockets;

useIlluminate\Contracts\Broadcasting\ShouldBroadcast;

classUserSubscribed

{

useInteractsWithSockets,SerializesModels;

/**

*Createaneweventinstance.

*

*@returnvoid

*/

publicfunction__construct()

{

//

}

/**

*Getthechannelstheeventshouldbebroadcaston.

*

*@returnChannel|array

*/

publicfunctionbroadcastOn()

{

returnnewPrivateChannel('channel-name');

}

}

Let’stakealookatwhatwegethere.SerializesModelsworksjustlikewithjobs;itallowsyoutoacceptEloquentmodelsasparameters.InteractsWithSockets,ShouldBroadcast,andthebroadcastOn()methodprovidethebackingfunctionalityforbroadcastingeventsusing

Page 619: Laravel: Up and Running: A Framework for Building Modern PHP

WebSockets,whichwe’llcoverinabit.

Itmightseemstrangethatthere’snohandle()orfire()methodhere.Butremember,thisobjectexistsnottodetermineaparticularaction,butjusttoencapsulatesomedata.Thefirstpieceofdataisitsname;UserSubscribedtellsusthataparticulareventhappened(ausersubscribed).Therestofthedataisanydatawepassintotheconstructorandassociatewiththisentity.

Example16-10showswhatwemightwanttodowithourUserSubscribedevent.

Example16-10.Injectingdataintoanevent...

classUserSubscribed

{

useInteractsWithSockets,SerializesModels;

public$user;

public$plan;

publicfunction__construct($user,$plan)

{

$this->user=$user;

$this->plan=$plan;

}

}

Nowwehaveanobjectthatappropriatelyrepresentstheeventthathappened:$event->usersubscribedtothe$event->planplan.

Page 620: Laravel: Up and Running: A Framework for Building Modern PHP

ListeningforanEventWehaveanevent,andtheabilitytofireit.Nowlet’slookathowtolistenforit.

First,we’llcreateaneventlistener.Let’ssaywewanttoemailtheapp’sownereverytimeanewusersubscribes:

phpartisanmake:listenerEmailOwnerAboutSubscription--event=UserSubscribed

ThatgivesusthefileinExample16-11.

Example16-11.ThedefaulttemplateforaLaraveleventlistener<?php

namespaceApp\Listeners;

useApp\Events\UserSubscribed;

useIlluminate\Queue\InteractsWithQueue;

useIlluminate\Contracts\Queue\ShouldQueue;

classEmailOwnerAboutSubscription

{

/**

*Createtheeventlistener.

*

*@returnvoid

*/

publicfunction__construct()

{

//

}

/**

*Handletheevent.

*

*@paramUserSubscribed$event

*@returnvoid

*/

publicfunctionhandle(UserSubscribed$event)

{

//

}

}

Thisiswheretheactionhappens—wherethehandle()methodlives.ThismethodexpectstobepassedaneventoftypeUserSubscribedandactinresponsetoit.

So,let’smakeitsendanemail(Example16-12).

Example16-12.Asampleeventlistener...

useIlluminate\Contracts\Mail\Mailer;

classEmailOwnerAboutSubscription

{

protected$mailer;

publicfunction__construct(Mailer$mailer)

{

$this->mailer=$mailer;

}

publicfunctionhandle(UserSubscribed$event)

{

Page 621: Laravel: Up and Running: A Framework for Building Modern PHP

$this->mailer->send(

newOwnerSubscriptionEmail($event->user,$event->plan)

);

}

}

Great!Now,onelasttask:weneedtosetthislisteneruptolistentotheUserSubscribedevent.We’llsetthatupinthe$listenpropertyoftheEventServiceProviderclass(seeExample16-13).

Example16-13.BindinglistenerstoeventsinEventServiceProviderclassEventServiceProviderextendsServiceProvider

{

protected$listen=[

\App\Events\UserSubscribed::class=>[

\App\Listeners\EmailOwnerAboutSubscription::class,

],

];

Asyoucansee,thekeyofeacharrayentryistheclassnameoftheevent,andthevalueisanarrayoflistenerclassnames.WecanaddasmanyclassnamesaswewantundertheUserSubscribedkeyandtheywillalllistenandrespondtoeachUserSubscribedevent.

EventsubscribersThere’sonemorestructureyoucanusetodefinetherelationshipbetweenyoureventsandtheirlisteners.Laravelhasaconceptcalledaneventsubscriber,whichisaclassthatcontainsacollectionofmethodsthatactasseparatelistenerstouniqueevents,andalsocontainsthemappingofwhichmethodshouldhandlewhichevent.Inthiscase,it’seasiertoshowthantotell;takealookatExample16-14.

Example16-14.Asampleeventsubscriber<?php

namespaceApp\Listeners;

classUserEventSubscriber

{

publicfunctiononUserSubscription($event)

{

//HandlestheUserSubscribedevent

}

publicfunctiononUserCancellation($event)

{

//HandlestheUserCancelledevent

}

publicfunctionsubscribe($events)

{

$events->listen(

\App\Events\UserSubscribed::class,

'App\Listeners\UserEventSubscriber@onUserSubscription'

);

$events->listen(

\App\Events\UserCancelled::class,

'App\Listeners\UserEventSubscriber@onUserCancellation'

);

}

}

Page 622: Laravel: Up and Running: A Framework for Building Modern PHP

Subscribersneedtodefineasubscribe()method,whichispassedaninstanceoftheeventdispatcher.We’llusethattopaireventswiththeirlisteners,butinthiscase,thosearemethodsonthisclass,insteadofentireclasses.Asarefresher,anytimeyouseean@inlinelikethismeanstheclassnameistotheleftofthe@andthemethodnameistotheright.

So,inExample16-14,we’redefiningthattheonUserSubscription()methodofthissubscriberwilllistentoanyUserSubscribedevents.

There’sonelastthingweneedtodo:inApp\Providers\EventServiceProvider,weneedtoaddoursubscriber ’sclassnametothe$subscribeproperty,asseeninExample16-15.

Example16-15.Registeringaneventsubscriber...

classEventServiceProviderextendsServiceProvider

{

...

protected$subscribe=[

\App\Listeners\UserEventSubscriber::class

];

}

Page 623: Laravel: Up and Running: A Framework for Building Modern PHP

BroadcastingEventsoverWebSockets,andLaravelEchoWebSocket(oftencalledWebSockets)isaprotocol,popularizedbyPusher,thatmakesitsimpletoprovidenear-real-timecommunicationbetweenwebdevices.RatherthanrelyingoninformationpassingviaHTTPrequests,WebSocketslibrariesopenadirectconnectionbetweentheclientandtheserver.WebSocketsarebehindtoolslikethechatboxesinGmailandFacebook.

WebSocketsworkbestwithsmallpiecesofdatapassedinapub/substructure—justlikeLaravel’sevents.Laravelhasabuilt-insetoftoolsthatmakeiteasytodefinethatoneormoreofyoureventsshouldbebroadcasttoaWebSocketserver;thismakesiteasy,forexample,tohaveaMessageWasReceivedeventthatispublishedtothenotificationsboxofacertainuserorsetofusers,theinstantamessagearrivesatyourapplication.

LARAVELECHO

Laravelalsohasamorepowerfultooldesignedformorecomplexeventbroadcasting.Ifyouneedpresencenotification,orwanttokeepyourrichfrontenddatamodelinsyncwithyourLaravelapp,checkoutLaravelEcho,whichwe’llcovertowardtheendofthischapter.MuchofwhatcomprisesEchoisbuiltintotheLaravelcore,whichwecoverin“AdvancedBroadcastingTools”,butsomeofitrequirespullingintheexternalJavaScriptEcholibrary,whichwecoverin“LaravelEcho(theJavaScriptSide)”.

Page 624: Laravel: Up and Running: A Framework for Building Modern PHP

ConfigurationandSetupTakealookatconfig/broadcasting.phptofindtheconfigurationsettingsforyoureventbroadcasting.Laravelsupportsthreedriversforbroadcasting:Pusher,apaidSaaSoffering;Redis,forlocallyrunWebSocketservers;andlog,forlocaldevelopmentanddebugging.

Page 625: Laravel: Up and Running: A Framework for Building Modern PHP

QUEUELISTENERSInorderforeventbroadcastingtomovequickly,Laravelpushestheinstructiontobroadcastthemontoaqueue.Thatmeansyou’llneedtohaveaqueueworkerrunning(orusethesyncqueuedriverforlocaldevelopment).See“RunningaQueueWorker”tolearnhowtorunaqueueworker.

Laravelsuggestsadefaultdelayofthreesecondsbeforethequeueworkerlooksfornewjobs.However,witheventbroadcasting,youmaynoticesomeeventstakeasecondortwotobroadcast.Tospeedthisup,updateyourqueuesettingstoonlywaitonesecondbeforelookingfornewjobs.

Page 626: Laravel: Up and Running: A Framework for Building Modern PHP

BroadcastinganEventTobroadcastanevent,youneedtomarkthateventasabroadcasteventbyhavingitimplementtheIlluminate\Contracts\Broadcasting\ShouldBroadcastinterface.ThisinterfacerequiresyoutoaddthebroadcastOn()method,whichwillreturnanarrayofeitherstringsorChannelobjects,eachrepresentingaWebSocketchannel.

THESTRUCTUREOFWEBSOCKETEVENTS

EveryeventyousendwithWebSocketscanhavethreeprimarycharacteristics:thename,thechannel,andthedata.

Thenameofaneventmightbesomethinglikeuser-was-subscribed,butLaravel’sdefaultistousethefullyqualifiedclassnameoftheevent;i.e.,sosomethinglikeApp\Events\UserSubscribed.YoucancustomizethisbypassingthenametotheoptionalbroadcastAs()methodinyoureventclass.

Thechannelisthewayofdescribingwhichclientsshouldreceivethismessage.It’saverycommonpatterntohaveachannelforeachuser(e.g.,users.1,users.2,etc.),andpossiblyachannelforallusers(e.g.,users),andmaybeoneforjustuserswhoaremembersofacertainaccount(accounts.1).Ifthechannelyou’retargetingisaprivatechannel,prefacethechannelnamewithprivate-,andifit’sapresencechannel,prefacethechannelnamewithpresence-.So,aprivatePusherchannelnamedgroups.5shouldbe,instead,private-groups.5.IfyouuseLaravel’sPrivateChannelandPresenceChannelobjectsinyourbroadcastOn()method,they’lltakecareofaddingthoseprefacestoyourchannelnamesforyou.

Thedataisapayload,usuallyJSON,ofinformationrelevanttotheevent—themessage,maybe,orinformationabouttheuserorplanthatcanbeacteduponbytheconsumingJavaScript.

Example16-16showsourUserSubscribedevent,modifiedtobroadcastontwochannels:onefortheuser(toconfirmtheuser ’ssubscription)andoneforadmins(tonotifythemofanewsubscription).

Example16-16.Aneventbroadcastingonmultiplechannels...

useIlluminate\Contracts\Broadcasting\ShouldBroadcast;

classUserSubscribedextendsEventimplementsShouldBroadcast

{

useInteractsWithSockets,SerializesModels;

public$user;

public$plan;

publicfunction__construct($user,$plan)

{

$this->user=$user;

$this->plan=$plan;

}

publicfunctionbroadcastOn()

{

//Stringsyntax

return[

'users.'.$this->user->id,

'admins'

];

//Channelobjectsyntax

return[

newChannel('users.'.$this->user->id),

newChannel('admins'),

Page 627: Laravel: Up and Running: A Framework for Building Modern PHP

//Ifitwereaprivatechannel:newPrivateChannel('admins'),

//Ifitwereapresencechannel:newPresenceChannel('admins'),

];

}

}

Bydefault,anypublicpropertiesofyoureventwillbeserializedasJSONandsentalongasthedataofyourbroadcastevent.ThatmeansthedataofoneofourbroadcastUserSubscribedeventsmightlooklikeExample16-17.

Example16-17.Samplebroadcasteventdata{

'user':{

'id':5,

'name':'FredMcFeely',

...

},

'plan':'silver'

}

YoucanoverridethisbyreturninganarrayofdatafromthebroadcastWith()methodonyourevent,asinExample16-18.

Example16-18.CustomizingthebroadcasteventdatapublicfunctionbroadcastWith()

{

return[

'userId'=>$this->user->id,

'plan'=>$this->plan

];

}

Finally,youcancustomizewhichqueueyoureventispushedontowithitsonQueue()method,asinExample16-19.Youmaychoosetodothissoyoucankeepotherqueueitemsfromslowingdownyoureventbroadcast;real-timeWebSocketsaren’tmuchfunifalong-runningjobthat’shigherinthequeuekeepstheeventsfromgoingoutintime.

Example16-19.SpecifyingthequeueajobshouldrunonpublicfunctiononQueue()

{

return'websockets-for-faster-processing'

}

Page 628: Laravel: Up and Running: A Framework for Building Modern PHP

ReceivingtheMessageIfyouchoosetohostyourownRedisWebSocketsserver,theLaraveldocshaveagreatwalkthroughonhowtosetthatupusingsocket.ioandioredis.

However,it’smuchmorecommontousePusher.Plansoveracertainsizecostmoney,butthere’sagenerousfreeplan.PushermakesitincrediblysimpletosetupasimpleWebSocketserver,anditsJavaScriptSDKhandlesalloftheauthenticationandchannelmanagementwithalmostnoworkonyourpart.SDKsareavailableforiOS,Android,andmanymoreplatforms,languages,andframeworks.

Page 629: Laravel: Up and Running: A Framework for Building Modern PHP
Page 630: Laravel: Up and Running: A Framework for Building Modern PHP

TOECHOORNOTTOECHO?ThenextsectioncovershowtowriteaJavaScriptfrontendtointeractwithLaraveloverWebSocketsbothwithandwithoutEcho.It’shelpfultounderstandhowtodothiswithoutEchoevenifyouchoosetouseitintheend,butbecausemuchofthecodehereisnotnecessaryifyouuseEcho,I’drecommendreadingthefollowingsection,thentheEchosection,“LaravelEcho(theJavaScriptSide)”,beforeyoustartimplementinganyofit;youcandecidewhichwayyoupreferandthenwriteyourcodefromthere.

Togetstarted,pullinPusher ’slibrary,getanAPIkeyfromyourPusheraccount,andsubscribetoanyeventsonanychannelswithcodelikethatinExample16-20.

Example16-20.BasicusageofPusherJS...

<scriptsrc="https://js.pusher.com/3.1/pusher.min.js"></script>

<script>

//Globally,perhaps;justasampleofhowtogetdatain

varApp={

'userId':5,

'pusherKey':'your-pusher-api-key-here'

};

//Locally

varpusher=newPusher(App.pusherKey);

varpusherChannel=pusher.subscribe('users.'+App.userId);

pusherChannel.bind('App\\Events\\UserSubscribed',(data)=>{

console.log(data.user,data.plan);

});

</script>

Page 631: Laravel: Up and Running: A Framework for Building Modern PHP
Page 632: Laravel: Up and Running: A Framework for Building Modern PHP

ESCAPINGBACKSLASHESINJAVASCRIPTSince\isacontrolcharacterinJavaScript,youneedtowrite\\torepresentabackslashinyourstrings,whichiswhytherearetwobackslashesbetweeneachnamespacesegmentinExample16-20.

TopublishtoPusherfromLaravel,getyourPusherkey,secret,andappIDfromyourPusheraccountdashboard,andthensettheminyour.envfileunderthekeysPUSHER_KEY,PUSHER_SECRET,andPUSHER_APP_ID.

Ifyouserveyourapp,visitapagewiththeJavaScriptfromExample16-20embeddedinitinonewindow,pushabroadcasteventinanotherwindoworfromyourterminal,haveaqueuelistenerrunningorareusingthesyncdriver,andallofyourauthenticationinformationissetupcorrectly,youshouldseeeventlogspoppingupinyourJavaScriptwindow’sconsoleinnearrealtime.

Withthispower,it’snoweasyforyoutokeepyourusersup-to-datewithwhat’shappeningwiththeirdataanytimethey’reinyourapp.Youcannotifyusersoftheactionsofotherusers,oflong-runningprocessesthathavejustfinished,orofyourapplication’sresponsestoexternalactionslikeincomingemailsorwebhooks.Thepossibilitiesareendless.

Page 633: Laravel: Up and Running: A Framework for Building Modern PHP
Page 634: Laravel: Up and Running: A Framework for Building Modern PHP

REQUIREMENTSIfyouwanttobroadcastwithPusherorRedis,you’llneedtobringinthesedependencies:

Pusher:pusher/pusher-php-server:~2.0

Redis:predis/predis:~1.0

Page 635: Laravel: Up and Running: A Framework for Building Modern PHP

AdvancedBroadcastingToolsLaravelhasafewmoretoolstomakeitpossibletoperformmorecomplexinteractionsineventbroadcasting.Thesetools,acombinationofframeworkfeaturesandaJavaScriptlibrary,arecalledLaravelEcho.

TheseframeworkfeaturesworkbestwhenyouuseLaravelEchoinyourJavaScriptfrontend(whichwe’llcoverin“LaravelEcho(theJavaScriptSide)”),butyoucanstillenjoysomeofthebenefitsofEchowithoutusingtheJavaScriptcomponents.EchowillworkwithbothPusherandRedis,butI’mgoingtousePusherforanyexamples.

ExcludingthecurrentuserfrombroadcasteventsEveryconnectiontoPusherisassignedaunique“socketID”identifyingthatsocketconnection.Andit’seasytodefinethatanygivensocket(user)shouldbeexcludedfromreceivingaspecifiedbroadcastevent.

Thisfeaturemakesitpossibletodefinethatcertaineventsshouldnotbebroadcasttotheuserwhofiredthem.Let’ssayeveryuserinateamgetsnotifiedwhenotheruserscreateatask;wouldyouwanttobenotifiedofataskyoujustcreated?No,andthat’swhywehavethetoOthers()method.

Toimplementthis,therearetwostepstofollow.First,youneedtosetupyourJavaScripttosendacertainPOSTto/broadcasting/socketwhenyourWebSocketconnectionisinitialized.Thisattachesyoursocket_idtoyourLaravelsession.Echodoesthisforyou,butyoucanalsodoitmanually—takealookattheEchosourcetoseehowitworks.

Next,you’llwanttoupdateeveryrequestthatyourJavaScriptmakestohaveanX-Socket-IDheaderthatcontainsthatsocket_id.Example16-21showshowtodothatinVueorjQuery.

Example16-21.SendingthesocketIDalongwitheachAjaxrequestinVueorjQuery//Runthisrightafteryouinitializeecho

//Vue

Vue.http.interceptors.push((request,next)=>{

request.headers['X-Socket-Id']=Echo.socketId();

next();

});

//jQuery

$.ajaxSetup({

headers:{

'X-Socket-Id':Echo.socketId()

}

});

Onceyou’vehandledthis,youcanexcludeanyeventfrombeingbroadcasttotheuserwhotriggereditbyusingthebroadcast()globalhelperinsteadoftheevent()globalhelperandthenchainingtoOthers()afterit:

broadcast(newUserSubscribed($user,$plan))->toOthers();

Page 636: Laravel: Up and Running: A Framework for Building Modern PHP

ThebroadcastserviceproviderAlloftheotherfeaturesthatEchoprovidesrequireyourJavaScripttoauthenticatewiththeserver.TakealookatApp\Providers\BroadcastServiceProvider,whereyou’lldefinehowtoauthorizeusers’accesstoyourprivateandpresencechannels.

Thetwoprimaryactionsyoucantakearetodefinethemiddlewarethatwillbeusedonyourbroadcastingauthroutes,andtodefinetheauthorizationsettingsforyourchannels.

Ifyou’regoingtousethesefeatures,you’llneedtouncommenttheApp\Providers\BroadcastServiceProvider::classlineinconfig/app.php.

Andifyou’llbeusingthesefeatureswithoutLaravelEcho,you’lleitherneedtomanuallyhandlesendingCSRFtokensalongwithyourauthenticationrequests,orexclude/broadcasting/authand/broadcasting/socketfromCSRFprotectionbyaddingthemtothe$exceptpropertyoftheVerifyCsrfTokenmiddleware.

BindingauthorizationdefinitionsforWebSocketchannelsPrivateandpresenceWebSocketchannelsneedtobeabletopingyourapplicationtolearnwhetherthecurrentuserisauthorizedforthatchannel.You’llusetheBroadcast::channel()methodtodefinetherulesforthisauthorization.

Page 637: Laravel: Up and Running: A Framework for Building Modern PHP
Page 638: Laravel: Up and Running: A Framework for Building Modern PHP

PUBLIC,PRIVATE,ANDPRESENCECHANNELSTherearethreetypesofchannelsinWebSockets:public,private,andpresence.

Publicchannelscanbesubscribedtobyanyuser,authenticatedornot.

Privatechannelsrequiretheenduser’sJavaScripttoauthenticateagainsttheapplicationtoprovethattheuserisbothauthenticatedandauthorizedtojointhischannel.

Presencechannelsareatypeofprivatechannel,butinsteadofallowingformessagepassing,theysimplykeeptrackofwhichusersjoinandleavethechannel,andmakethisinformationavailabletotheapplication’sfrontend.

Broadcast::channel()takestwoparameters:first,astringrepresentingthechannel(s)youwantittomatch,andsecond,aclosurethatdefineshowtoauthorizeusersforanychannelmatchingthatstring.TheclosurewillbepassedanEloquentmodelofthecurrentuserasitsfirstparameter,andanymatched*segmentsasadditionalparameters.Forexample,achannelauthorizationdefinitionwithastringofteams.*,whenmatchedagainstthechannelteams.5,willpassitsclosure$userasthefirstparameterand5asthesecondparameter.

Ifyou’redefiningtherulesforaprivatechannel,yourBroadcast::channel()closurewillneedtoreturnaboolean:isthisuserauthorizedforthischannelornot?Ifyou’redefiningtherulesforapresencechannel,yourclosureshouldreturnanarrayofdatayouwantavailabletothepresencechannelforanyusersthatyouwanttoshowupinthechannel.Example16-22illustratesdefiningrulesforbothkindsofchannel.

Example16-22.DefiningauthorizationrulesforprivateandpresenceWebSocketchannels...

classBroadcastServiceProviderextendsServiceProvider

{

publicfunctionboot()

{

...

//Definehowtoauthenticateaprivatechannel

Broadcast::channel('teams.*',function($user,$teamId){

return$user->team_id==$teamId;

});

//Definehowtoauthenticateapresencechannel;returnanydata

//youwanttheapptohaveabouttheuserinthechannel

Broadcast::channel('rooms.*',function($user,$roomId){

if($user->rooms->contains($roomId)){

return[

'name'=>$user->name

];

}

});

YoumightbewonderinghowthisinformationgetsfromyourLaravelapplicationtoyourJavaScriptfrontend.Pusher ’sJavaScriptlibrarysendsaPOSTtoyourapplication;bydefaultitwillhit/pusher/auth,butyoucancustomizethat(andEchocustomizesitforyou)tohitLaravel’sauthenticationroute,/broadcasting/auth:

varpusher=newPusher(App.pusherKey,{

authEndpoint:'/broadcasting/auth'

Page 639: Laravel: Up and Running: A Framework for Building Modern PHP

});

Example16-23showshowwecantweakExample16-20forprivateandpresencechannels,withoutEcho’sfrontendcomponents.

Example16-23.BasicusageofPusherJSforprivateandpresencechannels...

<scriptsrc="https://js.pusher.com/3.1/pusher.min.js"></script>

<script>

//Globally,perhaps;justasampleofhowtogetdatain

varApp={

'userId':{{auth()->user()->id}},

'pusherKey':'yourpusherkeyhere'

};

//Locally

varpusher=newPusher(App.pusherKey,{

authEndpoint:'/broadcasting/auth'

});

//Privatechannel

varprivateChannel=pusher.subscribe('private-teams.1');

privateChannel.bind('App\\Events\\UserSubscribed',(data)=>{

console.log(data.user,data.plan);

});

//Presencechannel

varpresenceChannel=pusher.subscribe('presence-rooms.5');

console.log(presenceChannel.members);

</script>

WenowhavetheabilitytosendWebSocketmessagestousersdependingonwhethertheypassagivenchannel’sauthorizationrules.Wecanalsokeeptrackofwhichusersareactiveinaparticulargrouporsectionofthesite,anddisplayrelevantinformationtoeachuseraboutotherusersinthesamegroup.

Page 640: Laravel: Up and Running: A Framework for Building Modern PHP

LaravelEcho(theJavaScriptSide)LaravelEchoiscomprisedoftwopieces:theadvancedframeworkfeatureswejustcovered,andaJavaScriptpackagethattakesadvantageofthosefeaturesanddrasticallyreducestheamountofboilerplatecodeyouneedtowritepowerfulWebSocket-basedfrontends.TheEchoJavaScriptpackagemakesiteasytohandleauthentication,authorization,andsubscribingtoprivateandpresencechannels.EchocanbeusedwiththeSDKsforeitherPusherJS(forPusher)orsocket.io(forRedis).

BringingEchointoyourprojectTouseEchoinyourproject’sJavaScript,addittopackage.jsonusingnpminstall--save(besuretobringintheappropriatePusherorsocket.ioSDKaswell):

npminstallpusher-js--save

npminstalllaravel-echo--save

Let’sassumeyouhaveabasicGulpfilerunningyourapp.jsfilethroughWebpack,likeinExample16-24.

Example16-24.Compilingapp.jsthroughWebpackconstelixir=require('laravel-elixir');

elixir(mix=>{

mix.webpack('app.js');

});

Now,createabasicresources/assets/js/app.jsfile(Example16-25)tobringinyourdependenciesandinitializeEcho.

Example16-25.InitializingEchoinapp.jsimportEchofrom"laravel-echo"

window.Echo=newEcho({

broadcaster:'pusher',

key:'your-pusher-key'

});

//AddyourEchobindingshere

ForCSRFprotection,you’llalsoneedtoaddacsrf-token<meta>tagtoyourHTMLtemplate:

<metaname="csrf-token"content="{{csrf_token()}}">

And,ofcourse,remembertolinktoyourcompiledapp.jsinyourHTMLtemplate:

<scriptsrc="/js/app.js"></script>

Nowwe’rereadytogetstarted.

Page 641: Laravel: Up and Running: A Framework for Building Modern PHP

UsingEchoforbasiceventbroadcastingThisisnothingdifferentfromwhatwe’vealreadyusedPusherJSfor,butExample16-26isasimplecodesampletoshowhowtouseEchotolistentopublicchannelsforbasiceventinformation.

Example16-26.ListeningtoapublicchannelwithEchovarcurrentTeamId=5;//Likelysetelsewhere

Echo.channel('teams.'+currentTeamId)

.listen('UserSubscribed',(data)=>{

console.log(data);

});

Echoprovidesafewmethodsforsubscribingtovarioustypesofchannels;channel()willsubscribeyoutoapublicchannel.NotethatwhenyoulistentoaneventwithEcho,youcanignorethefulleventnamespaceandjustlistenfortheuniquenameofthisevent.Andnowwenowhaveaccesstothepublicdatathat’spassedalongwithourevent.

Wecanalsochainlisten()handlers,asinExample16-27.

Example16-27.ChainingeventlistenersinEchoEcho.channel('teams.'+currentTeamId)

.listen('UserSubscribed',(data)=>{

console.log(data);

})

.listen('UserCanceled',(data)=>{

console.log(data);

});

Page 642: Laravel: Up and Running: A Framework for Building Modern PHP
Page 643: Laravel: Up and Running: A Framework for Building Modern PHP

REMEMBERTOCOMPILEANDINCLUDE!Didyoutrythesecodesamplesandnotseeanythingchangeinyourbrowser?Makesuretorungulp(ifyou’rerunningitonce)orgulpwatch(torunalistener)tocompileyourcode.And,ifyouhaven’tyet,besuretoactuallyincludeapp.jsinyourtemplatesomewhere.

PrivatechannelsandbasicauthenticationEchoalsohasamethodforsubscribingtoprivatechannels:private().Itworksthesameaschannel(),butitrequiresyoutohavesetupchannelauthorizationdefinitionsinBroadcastServiceProvider,likewecoveredearlier.Additionally,unlikewiththeSDKs,youdon’tneedtoputprivate-infrontofyourchannelname.

Example16-28showswhatitlooksliketolistentoaprivatechannelnamedprivate-teams.5.

Example16-28.ListeningtoaprivatechannelwithEchovarcurrentTeamId=5;//Likelysetelsewhere

Echo.private('teams.'+currentTeamId)

.listen('UserSubscribed',(data)=>{

console.log(data);

});

PresencechannelsEchomakesitmuchsimplertojoinandlistentoeventsinpresencechannels.Thistimeyou’llwanttousethejoin()methodtobindtothischannel,asinExample16-29.

Example16-29.JoiningapresenceofchannelvarcurrentTeamId=5;//Likelysetelsewhere

Echo.join('teams.'+currentTeamId)

.here((members)=>{

console.log(members);

});

join()subscribestothepresencechannel,andhere()allowsyoutodefinethebehaviorwhentheuserjoinsandalsowhenanyotherusersjoinorleavethepresencechannel.

Youcanthinkofapresencechannellikea“who’sonline”sidebarinachatroom.Whenyoufirstjoinapresencechannel,yourhere()callbackwillbecalledandprovidedalistofallthemembersatthattime.Andanytimeanymembersjoinorleave,thatcallbackwillbecalledagainwiththeupdatedlist.There’snomessaginghappeninghere,butyoucanplaysounds,updatetheon-pagelistofmembers,ordowhateverelseyouwantinresponsetotheseactions.

Therearealsospecificmethodsforindividualevents,whichyoucanuseindividuallyorchained(seeExample16-30).

Example16-30.ListeningforspecificpresenceeventsvarcurrentTeamId=5;//Likelysetelsewhere

Echo.join('teams.'+currentTeamId)

Page 644: Laravel: Up and Running: A Framework for Building Modern PHP

.then((members)=>{

//runswhenyoujoin

console.table(members);

})

.joining((joiningMember,members)=>{

//runswhenanothermemberjoins

console.table(joiningMember);

})

.leaving((leavingMember,members)=>{

//runswhenanothermemberleaves

console.table(leavingMember);

});

ExcludingthecurrentuserWecoveredthispreviouslyinthechapter,butifyouwanttoexcludethecurrentuser,usethebroadcast()globalhelperinsteadoftheevent()globalhelperandthenchainthetoOthers()methodafteryourbroadcastcall.

Asyoucansee,theEchoJavaScriptlibrarydoesn’tdoanythingyoucouldn’tdoonyourown—butitmakesalotofcommontasksmuchsimpler,andprovidesacleaner,moreexpressivesyntaxforcommonWebSockettasks.

SubscribingtonotificationswithEchoLaravel’snotificationscomewithabroadcastdriveroutoftheboxthatpushesnotificationsoutasbroadcastevents.YoucansubscribetothesenotificationswithEchousingEcho.notification,asinExample16-31.

Example16-31.SubscribingtoanotificationwithEchoEcho.private('App.User.'+userId)

.notification((notification)=>{

console.log(notification.type);

});

Page 645: Laravel: Up and Running: A Framework for Building Modern PHP

SchedulerIfyou’veeverwrittenacronjobbefore,youlikelyalreadywishforabettertool.Notonlyisthesyntaxonerousandfrustratinglydifficulttoremember,butit’sonesignificantaspectofyourapplicationthatcan’tbestoredinversioncontrol.

Laravel’sschedulermakeshandlingscheduledtaskssimple.You’llwriteyourscheduledtasksincode,andthenpointonecronjobatyourapp:onceperminute,runphpartisanschedule:run.EverytimethisArtisancommandisrun,Laravelchecksyourscheduledefinitionstofindoutifanyscheduledtasksshouldrun.

Here’sthecronjobtodefinethatcommand:

*****php/home/myapp.com/artisanschedule:run>>/dev/null2>&1

Therearemanytasktypesyoucanscheduleandmanytimeframesyoucanusetoschedulethem.

app/Console/Kernel.phphasamethodnamed$schedule,whichiswhereyou’lldefineanytasksyou’dliketoschedule.

Page 646: Laravel: Up and Running: A Framework for Building Modern PHP

AvailableTaskTypesFirst,let’stakealookatthesimplestoption:aclosure,runeveryminute(Example16-32).Thatmeansthat,everytimethecronjobhitstheschedule:runcommand,itwillcallthisclosure.

Example16-32.Schedulingaclosuretorunonceeveryminute//app/Consoles/Kernel.php

publicfunctionschedule($schedule)

{

$schedule->call(function(){

dispatch(newCalculateTotals);

})->everyMinute();

}

Therearetwoothertypesoftasksyoucanschedule:Artisanandshellcommands.

YoucanscheduleArtisancommandsbypassingtheirsyntaxexactlyasyouwouldcallthemfromthecommandline:

$schedule->command('scores:tally--reset-cache')->everyMinute();

AndyoucanrunanyshellcommandsthatyoucouldrunwithPHP’sexec()method:

$schedule->exec('/home/myapp.com/bin/build.sh')->everyMinute();

Page 647: Laravel: Up and Running: A Framework for Building Modern PHP

AvailableTimeFramesThebeautyoftheschedulerisn’tjustthatyoucandefineyourtasksincode;it’sthatyoucanschedulethemincode,too.Laravelkeepstrackoftimepassingandevaluateswhetherit’stimeforanygiventasktorun.That’seasywitheveryMinute()becausetheanswerisalwayssimple:runthetask.ButLaravelkeepstherestsimpleforyou,too,evenforthemostcomplexofrequests.

Let’stakealookatyouroptionsbystartingwithamonstrousdefinitionthat’ssimpleinLaravel:

$schedule->call(function(){

//RunsonceaweekonSundayat23:50

})->weekly()->sundays()->at('23:50');

Noticethatwecanchaintimestogether:wecandefinefrequencyandspecifythedayoftheweekandthetime,andofcoursewecandosomuchmore.

Table16-1showsalistofpotentialdate/timemodifiersforusewhenschedulingajob.

Table16-1.Date/timemodifiersforusewiththescheduler

Command Description

->timezone('America/Detroit') Setthetimezoneforschedules

->cron('******') Definethescheduleusingthetraditionalcronnotation

->everyMinute() Runeveryminute

->everyFiveMinutes() Runevery5minutes

->everyTenMinutes() Runevery10minutes

->everyThirtyMinutes() Runevery30minutes

->hourly() Runeveryhour

->daily() Runeverydayatmidnight

->dailyAt('14:00') Runeverydayat14:00

->twiceDaily(1,14) Runeverydayat1:00and14:00

->weekly() Runeveryweek(midnightonSunday)

->weeklyOn(5,'10:00') RuneveryweekonFridayat10:00

->monthly() Runeverymonth(midnightonthe1st)

->monthlyOn(15,'23:00') Runeverymonthonthe15that23:00

Page 648: Laravel: Up and Running: A Framework for Building Modern PHP

->quarterly() Runeveryquarter(midnightonthe1stofJanuary,April,July,andOctober)

->yearly() Runeveryyear(midnightonthe1stofJanuary)

->when(closure) Limitthetasktowhenclosurereturnstrue

->skip(closure) Limitthetasktowhenclosurereturnsfalse

->between('8:00','12:00') Limitthetasktobetweenthegiventimes

->unlessBetween('8:00','12:00') Limitthetasktoanytimeexceptbetweenthegiventimes

->weekdays() Limittoweekdays

->sundays() LimittoSundays

->mondays() LimittoMondays

->tuesdays() LimittoTuesdays

->wednesdays() LimittoWednesdays

->thursdays() LimittoThursdays

->fridays() LimittoFridays

->saturdays() LimittoSaturdays

Mostofthesecanbechainedoneafteranother,butofcourse,anycombinationsthatdon’tmakesensechainedcan’tbechained.

Example16-33showsafewcombinationsyoucouldconsider.

Example16-33.Somesamplescheduledevents//BothrunweeklyonSundayat23:50

$schedule->command('do:thing')->weeklyOn(0,'23:50');

$schedule->command('do:thing')->weekly()->sundays()->at('23:50');

//Runonceperhour,weekdays,8am-5pm

$schedule->command('do:thing')->weekdays()->hourly()->when(function(){

returndate('H')>=8&&date('H')<=17;

});

//Runonceperhour,weekdays,8am-5pmusingnewLaravel5.3"between"

$schedule->command('do:thing')->weekdays()->hourly()->between('8:00','17:00');

$schedule->command('do:thing')->everyThirtyMinutes()->skip(function(){

returnapp('SkipDetector')->shouldSkip();

});

Page 649: Laravel: Up and Running: A Framework for Building Modern PHP

BlockingandOverlapIfyouwanttoavoidyourtasksoverlappingeachother—forexample,ifyouhaveataskrunningeveryminutethatmaysometimestakelongerthanaminutetorun—endtheschedulechainwiththewithoutOverlapping()method.Thismethodskipsataskifthepreviousinstanceofthattaskisstillrunning:

$schedule->command('do:thing')->everyMinute()->withoutOverlapping();

Page 650: Laravel: Up and Running: A Framework for Building Modern PHP

HandlingTaskOutputSometimestheoutputfromyourscheduledtaskisimportant,whetherforlogging,notifications,orjustensuringthatthetaskran.

Ifyouwanttowritethereturnedoutputofatasktoafile,usesendOutputTo():

$schedule->command('do:thing')->daily()->sendOutputTo($filePath);

Ifyouwanttoappendittoafileinstead,useappendOutputTo():

$schedule->command('do:thing')->daily()->appendOutputTo($filePath);

Andifyouwanttoemailtheoutputtoadesignatedrecipient,writeittoafilefirstandthenaddemailOutputTo():

$schedule->command('do:thing')

->daily()

->sendOutputTo($filePath)

->emailOutputTo('[email protected]');

MakesurethatyouremailsettingsareconfiguredcorrectlyinLaravel’sbasicemailconfiguration.

Page 651: Laravel: Up and Running: A Framework for Building Modern PHP
Page 652: Laravel: Up and Running: A Framework for Building Modern PHP

CLOSURESCHEDULEDEVENTSCAN’TSENDOUTPUTThesendOutputTo(),appendOutputTo(),andemailOutputTo()methodsonlyworkforcommandscheduledtasks.Youcan’tusethemforclosures,unfortunately.

Youmayalsowanttosendsomeoutputtoensurethatyourtasksrancorrectly.Thereareafewservicesthatprovidethissortofuptimemonitoring,mostsignificantlyLaravelEnvoyer(azero-downtimedeploymentservicethatalsoprovidescronuptimemonitoring)andDeadMan’sSnitch,atooldesignedpurelyformonitoringcronjobuptime.

Theseservicesdon’texpectsomethingtobeemailedtothem,butratherexpectanHTTP“ping,”soLaravelmakesthateasywithpingBefore()andthenPing():

$schedule->command('do:thing')

->daily()

->pingBefore($beforeUrl)

->thenPing($afterUrl);

Ifyouwanttousethepingfeatures,you’llneedtopullinGuzzleusingComposer:"guzzlehttp/guzzle":"~5.3|~6.0".

Page 653: Laravel: Up and Running: A Framework for Building Modern PHP

TaskHooksSpeakingofrunningsomethingbeforeandafteryourtask,therearehooksforthat,withbefore()andafter():

$schedule->command('do_thing')

->daily()

->before(function(){

//Prepare

})

->after(function(){

//Cleanup

});

Page 654: Laravel: Up and Running: A Framework for Building Modern PHP

TestingTestingqueuedjobs(oranythingelseinthequeue)iseasy.Inphpunit.xml,whichistheconfigurationfileforyourtests,theQUEUE_DRIVERenvironmentvariableissettosyncbydefault.Thatmeansyourtestswillrunyourjobsorotherqueuedtaskssynchronously,directlyinyourcode,withoutrelyingonaqueuesystemofanysort.Youcantestthemjustlikeanyothercode.

However,ifyou’djustliketocheckthatajobwasfired,youcandothatwiththeexpectsJobs()method,asinExample16-34.

Example16-34.Assertingthatajobofthespecifiedclasswasdispatchedpublicfunctiontest_changing_number_of_subscriptions_crunches_reports()

{

$this->expectsJobs(App\Jobs\CrunchReports::class);

...

}

Or,inLaravel5.3andlater,youcanassertagainstthespecificjobitself,asinExample16-35.

Example16-35.UsingaclosuretoverifythatadispatchedjobmeetsgivencriteriauseIlluminate\

publicfunctiontest_changing_subscriptions_triggers_crunch_job()

{

...

Bus::assertDispatched(CrunchReports::class,function($e){

return$e->subscriptions->contains(5);

});

//AlsocanuseassertNotDispatched

}

Totestthataneventfired,youhavethreeoptions.First,youcanjusttestthatthebehavioryouexpectedhappened,withoutconcerningyourselfwiththeeventitself.

Second,youcanexplicitlyassertthattheeventfired,asinExample16-36.ThisworksinLaravel5.2.

Example16-36.Assertingthataneventofthespecifiedclasswasfiredpublicfunctiontest_usersubscribed_event_fires()

{

$this->expectsEvents(App\Events\UserSubscribed::class);

...

}

Finally,youcanrunatestagainsttheeventthatwasfired,asinExample16-37.ThisisnewinLaravel5.3.

Example16-37.Usingaclosuretoverifythatafiredeventmeetsgivencriteriapublicfunctiontest_usersubscribed_event_fires()

{

...

Event::assertFired(UserSubscribed::class,function($e){

return$e->user->email='[email protected]';

Page 655: Laravel: Up and Running: A Framework for Building Modern PHP

});

//AlsocanuseassertNotFired()

}

Anothercommonscenarioisthatyou’retestingcodethatincidentallyfiresevents,andyouwanttodisabletheeventlistenersduringthattest.YoucandisabletheeventsystemwiththewithoutEvents()method,asinExample16-38.

Example16-38.Disablingeventlistenersduringatestpublicfunctiontest_something_subscription_related()

{

$this->withoutEvents();

...

}

Page 656: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRQueuesallowyoutoseparatechunksofyourapplication’scodefromthesynchronousflowofuserinteractionsouttoalistofcommandstobeprocessedbya“queueworker.”Thisallowsyouruserstoresumeinteractionswithyourapplicationwhileslowerprocessesarehandledasychronouslyinthebackground.

Jobsareclassesthatarestructuredwiththeintentionofencapsulatingachunkofapplicationbehaviorsothatitcanbepushedontoaqueue.

Laravel’seventsystemfollowsthepub/suborobserverpattern,allowingyoutosendoutnotificationsofaneventfromonepartofyourapplication,andelsewherebindlistenerstothosenotificationstodefinewhatbehaviorshouldhappeninresponsetothem.UsingWebSockets,eventscanalsobebroadcasttofrontendclients.

Laravel’sschedulersimplifiesschedulingtasks.Pointanevery-minutecronjobtophpartisanschedule:runandthenscheduleyourtaskswitheventhemostcomplexoftimerequirementsusingthescheduler,andLaravelwillhandleallthetimingsforyou.

Page 657: Laravel: Up and Running: A Framework for Building Modern PHP

Chapter17.HelpersandCollections

We’vealreadycoveredmanyglobalfunctionsthroughoutthebook:littlehelpersthatmakeiteasiertoperformcommontasks,likedispatch()forJobs,event()forEvents,app()fordependencyresolution,andmanymore.WealsotalkedabitaboutLaravel’scollections,orarraysonsteroids,inChapter8.

Inthischapterwe’llcoversomeofthemorecommonandpowerfulhelpersandsomeofthebasicsofprogrammingwithcollections.

Page 658: Laravel: Up and Running: A Framework for Building Modern PHP

HelpersYoucanfindafulllistofthehelpersLaraveloffersinthehelpersdocs,butwe’regoingtocoverafewofthemostusefulfunctionshere.

Page 659: Laravel: Up and Running: A Framework for Building Modern PHP

ArraysPHP’snativearraymanipulationfunctionsgiveyoualotofpower,butsometimestherearecommonmanipulationswewanttomakethatrequireunwieldyloopsandlogicchecks.Laravel’sarrayhelpersmakeafewcommonarraymanipulationsmuchsimpler:

array_first($array,$closure,$default=null)

Returnsthefirstarrayvaluethatpassesatest,definedinaclosure.Youcanoptionallysetthedefaultvalueasthethirdparameter:

$people=[

[

'email'=>'[email protected]',

'name'=>'MalcolmMe'

],

[

'email'=>'[email protected]',

'name'=>'JamesJo'

]

];

$value=array_first($people,function($key,$person){

return$person['email']=='[email protected]';

});

array_get($array,$key,$default=null)

Makesiteasytogetvaluesoutofanarray,withtwoaddedbenefits:itwon’tthrowanerrorifyouaskforakeythatdoesn’texist(andyoucanprovidedefaultswiththethirdparameter),andyoucanusedotnotationtotraversenestedarrays.Forexample:

$array=['owner'=>['address'=>['line1'=>'123MainSt.']]];

$line1=array_get($array,'owner.address.line1','Noaddress');

$line2=array_get($array,'owner.address.line2');

array_has($array,$key)

Makesiteasytocheckwhetheranarrayhasaparticularvaluesetusingdotnotationfortraversingnestedarrays:

$array=['owner'=>['address'=>['line1'=>'123MainSt.']]];

if(array_has($array,'owner.address.line2')){

//Dostuff

}

array_pluck($array,$key,$indexKey)

Returnsanarrayofthevaluescorrespondingtotheprovidedkey:

$array=[

['owner'=>['id'=>4,'name'=>'Tricia']],

['owner'=>['id'=>7,'name'=>'Kimberly']],

];

Page 660: Laravel: Up and Running: A Framework for Building Modern PHP

$array=array_pluck($array,'owner.name');

//Returns['Tricia','Kimberly'];

Ifyouwantthereturnedarraytobekeyedbyanothervaluefromthesourcearray,youcanpassthatvalue’sdot-notatedreferenceasthethirdparameter:

$array=array_pluck($array,'owner.name','owner.id');

//Returns[4=>'Tricia',7=>'Kimberly'];

Page 661: Laravel: Up and Running: A Framework for Building Modern PHP

StringsJustlikewitharrays,therearesomestringmanipulationsandchecksthatarepossiblewithnativePHPfunctions,butcanbecumbersome.Laravel’shelpersmakeafewcommonstringoperationsfasterandsimpler:

e($string)

Analiastohtmlentities();preparesa(oftenuser-provided)stringforsafeechoingonanHTMLpage.Forexample:

e('<script>dosomethingnefarious</script>');

//Returns&lt;script&gt;dosomethingnefarious&lt;/script&gt;

starts_with($haystack,$needle),ends_with($haystack,$needle),andstr_contains($haystack,$needle)

Returnabooleanofwhethertheprovided“haystack”stringstartswith,endswith,orcontainstheprovided“needle”string:

if(starts_with($url,'https')){

//Dosomething

}

if(ends_with($abstract,'...')){

//Dosomething

}

if(str_contains($description,'1337h4x0r')){

//Runaway

}

str_limit($string,$numCharacters,$concatenationString='...')

Limitsastringtotheprovidednumberofcharacters.Ifthestringislessthanthelimit,justreturnsthestring;ifit’sgreater,trimstothenumberofcharactersprovidedandthenappendseither...ortheprovidedconcatenationstring.Forexample:

$abstract=str_limit($loremIpsum,30);

//Returns"Loremipsumdolorsitamet,co..."

$abstract=str_limit($loremIpsum,30,"&hellip;");

//Returns"Loremipsumdolorsitamet,co&hellip;"

str_is($pattern,$string)

Returnsabooleanofwhetherornotagivenstringmatchesagivenpattern.Thepatterncanbearegexpattern,oryoucanuseasteriskstoindicatewildcardpositions:

str_is('*.dev','myapp.dev');//true

str_is('*.dev','myapp.dev.co.uk');//false

str_is('*dev*','myapp.dev');//true

Page 662: Laravel: Up and Running: A Framework for Building Modern PHP

str_is('*myapp*','www.myapp.dev');//true

str_is('my*app','myfantasticapp');//true

str_is('my*app','myapp');//true

Page 663: Laravel: Up and Running: A Framework for Building Modern PHP

HOWTOPASSAREGEXTOSTR_IS()Ifyou’recuriousaboutwhatregexpatternsareacceptabletopasstostr_is(),checkoutthefunctiondefinitionhere(shortenedforspace)toseehowitworks.Notethatit’sanaliasofIlluminate\Support\Str::is:

publicfunctionis($pattern,$value)

{

if($pattern==$value)returntrue;

$pattern=preg_quote($pattern,'#');

$pattern=str_replace('\*','.*',$pattern);

return(bool)preg_match(

'#^'.$pattern.'\z#u',

$value

);

}

str_random($length)

Returnsarandomstringofalphanumericmixed-casecharactersofthelengthspecified:

$hash=str_random(64);

//Sample:J40uNWAvY60wE4BPEWxu7BZFQEmxEHmGiLmQncj0ThMGJK7O5Kfgptyb9ulwspmh

str_slug($string,$separator='-')

ReturnsaURL-friendlyslugfromastring—oftenusedforcreatingaURLsegmentforanameortitle:

str_slug('HowtoWinFriendsandInfluencePeople');

//Returns'how-to-win-friends-and-influence-people'

Page 664: Laravel: Up and Running: A Framework for Building Modern PHP

ApplicationPathsWhenyou’redealingwiththefilesystem,itcanoftenbetedioustomakelinkstocertaindirectoriesforgettingandsavingfiles.Thesehelpersgiveyouquickaccesstofindthefullyqualifiedpathstosomeofthemostimportantdirectoriesinyourapp.

Notethateachofthesecanbecalledwithnoparameters,butifaparameterispassed,itwillbeappendedtothenormaldirectorystringandreturnedasawhole:

app_path($append='')

Returnsthepathfortheappdirectory:

app_path();

//Returns/home/forge/myapp.com/app

base_path($append='')

Returnsthepathfortherootdirectoryofyourapp:

base_path();

//Returns/home/forge/myapp.com

config_path($append='')

Returnsthepathforconfigurationfilesinyourapp:

config_path();

//Returns/home/forge/myapp.com/config

database_path($append='')

Returnsthepathfordatabasefilesinyourapp:

database_path();

//Returns/home/forge/myapp.com/database

storage_path($append='')

Returnsthepathforthestoragedirectoryinyourapp:

storage_path();

//Returns/home/forge/myapp.com/storage

Page 665: Laravel: Up and Running: A Framework for Building Modern PHP

URLsSomefrontendfilepathsareconsistentbutattimesannoyingtotype—forexample,pathstoassets—andit’shelpfultohaveconvenientshortcutstothem,whichwe’llcoverhere.ButsomecanactuallyvaryasroutedefinitionsmoveornewfilesareversionedwithElixir,sosomeofthesehelpersarevitalinmakingsureallofyourlinksandassetsworkcorrectly:

action('Controller@method’,$params=[],$absolute=true)

AssumingacontrollermethodhasasingleURLmappedtoit,returnsthecorrectURLgivenacontrollerandmethodnamepair(separatedby@):

<ahref="{{action('PeopleController@index'}}">SeeallPeople</a>

//Returns<ahref="http://myapp.com/people">SeeallPeople</a>

Ifthecontrollermethodrequiresparameters,youcanpasstheminasthesecondparameter(asanarray,ifthere’smorethanonerequiredparameter).Youcankeythemifyouwantforclarity,butwhatmattersisjustthatthey’reintherightorder:

<ahref="{{action('PeopleController@show',['id'=>3]}}">SeePerson#3</a>

//or

<ahref="{{action('PeopleController@show',[3]}}">SeePerson#3</a>

//Returns<ahref="http://myapp.com/people/3">SeePerson#3</a>

Ifyoupassfalsetothethirdparameter,yourlinkswillgenerateasrelative(/people/3)insteadofabsolute(http://myapp.com/people/3).

route($routeName,$params=[],$absolute=true)

Ifaroutehasaname(usingasintheroutedefinition),returnstheURLforthatroute:

<ahref="{{route('people.index')}}">SeeallPeople</a>

//Returns<ahref="http://myapp.com/people">SeeallPeople</a>

Iftheroutedefinitionrequiresparameters,youcanpasstheminasthesecondparameter(asanarrayifmorethanoneparameterisrequired).Again,youcankeythemifyouwantforclarity,butwhatmattersisjustthatthey’reintherightorder:

<ahref="{{action('people.show',['id'=>3])}}">SeePerson#3</a>

//or

<ahref="{{action('people.show',[3])}}">SeePerson#3</a>

//Returns<ahref="http://myapp.com/people/3">SeePerson#3</a>

Page 666: Laravel: Up and Running: A Framework for Building Modern PHP

Ifyoupassfalsetothethirdparameter,yourlinkswillgenerateasrelativeinsteadofabsolute.

url($string)andsecure_url($string)Givenanypathstring,convertstoafullyqualifiedURL.secure_url()isthesameasurl()butforcesHTTPS.

url('people/3');

//Returnshttp://myapp.com/people/3

Ifnoparametersarepassed,thisinsteadgivesaninstanceofIlluminate\Routing\UrlGenerator,whichmakesmethodchainingpossible:

url()->current();

//Returnshttp://myapp.com/abc

url()->full();

//Returnshttp://myapp.com/abc?order=reverse

url()->previous();

//Returnshttp://myapp.com/login

//AndmanymoremethodsavailableontheUrlGenerator...

elixir($filePath)

IfassetsareversionedwithElixir ’sversioningsystem,giventhenonversionedpathname,returnsthefullyqualifiedURLfortheversionedfile:

<linkrel="stylesheet"href="{{elixir('css/app.css')}}">

//Returnssomethinglike/build/css/app-eb555e38.css

Page 667: Laravel: Up and Running: A Framework for Building Modern PHP

MiscThereareafewotherglobalhelpersthatI’drecommendgettingfamiliarwith.Ofcourse,youshouldcheckoutthewholelist,buttheonesmentionedherearedefinitelyworthtakingalookat:

abort($code,$message,$headers),abort_unless($boolean,$code,$message,$headers),andabort_if($boolean,$code,$message,$headers)

ThrowHTTPexceptions.abort()throwstheexceptiondefined,abort_unless()throwsitifthefirstparameterisfalse,andabort_if()throwsitifthefirstparameteristrue:

publicfunctioncontrollerMethod(Request$request)

{

abort(403,'Youshallnotpass');

abort_unless($request->has('magicToken'),403);

abort_if($request->user()->isBanned,403);

}

auth()

ReturnsaninstanceoftheLaravelauthenticator.LiketheAuthfacade,youcanusethistogetthecurrentuser,tocheckforloginstate,andmore:

$user=auth()->user();

if(auth()->check()){

//Dosomething

}

back()

Generatesa“redirectback”response,sendingtheusertothepreviouslocation:

Route::get('post',function(){

...

if($condition){

returnback();

}

});

collect($array)

Takesanarrayandreturnsthesamedata,convertedtoacollection:

$collection=collect(['Rachel','Hototo']);

We’llcovercollectionsinjustabit.

config($key)

Returnsthevalueforanydot-notatedconfigurationitem:

Page 668: Laravel: Up and Running: A Framework for Building Modern PHP

$defaultDbConnection=config('database.default');

csrf_field()andcsrf_token()ReturnafullHTMLhiddeninput(csrf_field())orjusttheappropriatetokenvalue(csrf_token())foraddingCSRFverificationtoyourformsubmission:

<form>

{{csrf_field()}}

</form>

//or

<form>

<inputtype="hidden"name="_token"value="{{csrf_token()}}">

</form>

dd($variable...)

Shortfor“dumpanddie,”runsvar_dump()onallprovidedparametersandthenexit()toquittheapplication(thisisusedfordebugging):

...

dd($var1,$var2,$state);//Whyisthisnotworking???

env($key,$default=null)

Returnstheenvironmentvariableforthegivenkey:

$key=env('API_KEY','');

Page 669: Laravel: Up and Running: A Framework for Building Modern PHP
Page 670: Laravel: Up and Running: A Framework for Building Modern PHP

USINGENV()OUTSIDEOFCONFIGFILESCertainfeaturesinLaravel,includingsomecachingandoptimizationfeatures,aren’tavailableifyouuseenv()callsanywhereoutsideofconfigfiles.

Thebestwaytopullinenvironmentvariablesistosetupconfigitemsforanythingyouwanttobeenvironment-specific.Havethoseconfigitemsreadtheenvironmentvariables,andthenreferencetheconfigvariablesanywherewithinyourapp:

//config/services.php

return[

'bugsnag'=>[

'key'=>env('BUGSNAG_API_KEY')

]

];

//incontroller,orwhatever

$bugsnag=newBugsnag(config('services.bugsnag.key'));

dispatch($job)

Dispatchesajob:

dispatch(newEmailAdminAboutNewUser($user));

event($event)

Firesanevent:

event(newContactAdded($contact));

factory($entityClass)

Returnsaninstanceofthefactorybuilderforthegivenclass:

$contact=factory(App\Contact::class)->make();

old($key,$default=null)

Returnstheoldvalue(fromthelastuserformsubmission)forthisformkey,ifitexists:

<inputname="name"value="{{old('value','Yournamehere')}}"

redirect($path)

Returnsaredirectresponsetothegivenpath:

Route::get('post',function(){

...

returnredirect('home');

});

Page 671: Laravel: Up and Running: A Framework for Building Modern PHP

Withoutparameters,generatesaninstanceoftheIlluminate\Routing\Redirectorclass.

response($body,$status,$headers)

Ifpassedwithparameters,returnsaprebuiltinstanceofResponse.Ifpassedwithnoresponse,returnsaninstanceoftheResponsefactory:

returnresponse('OK',200,['X-Header-Greatness'=>'Supergreat']);

returnresponse()->json(['status'=>'success'],200);

view($viewPath)

Returnsaviewinstance:

Route::get('home',function(){

returnview('home');//Gets/resources/views/home.blade.php

});

Page 672: Laravel: Up and Running: A Framework for Building Modern PHP

CollectionsCollectionsareoneofthemostpowerfulandyetunderappreciatedtoolsLaravelprovides.Wecoveredthemabitin“EloquentCollections”,buthere’saquickrecap.

Collectionsareessentiallyarrayswithsuperpowers.Thearray-traversingmethodsyounormallyhavetopassarraysinto(array_walk(),array_map(),array_reduce(),etc.),allofwhichhaveconfusinglyinconsistentmethodsignatures,areavailableasconsistent,clean,chainablemethodsoneverycollection.Youcangetatasteoffunctionalprogrammingandmap,reduce,andfilteryourwaytocleanercode.

We’llcoversomeofthebasicsofLaravel’scollectionsandcollectionpipelineprogramminghere,butforamuchdeeperoverview,checkoutAdamWathan’sbookRefactoringtoCollections(Gumroad).

Page 673: Laravel: Up and Running: A Framework for Building Modern PHP

TheBasicsofCollectionsCollectionsarenotanewideawithinLaravel.Manylanguagesmakecollection-styleprogrammingavailableonarraysoutofthebox,butwithPHPwe’renotquitesolucky.

UsingPHP’sarray*()functions,wecantakethemonstrosityshowninExample17-1,andturnitintotheslightlylessmonstrousmonstrosityshowninExample17-2.

Example17-1.Acommon,butugly,foreachloop$users=[...];

$admins=[];

foreach($usersas$user){

if($user['status']=='admin'){

$user['name']=$user['first'].''.$user['last'];

$admins[]=$user;

}

}

return$admins;

Example17-2.RefactoringtheforeachloopwithnativePHPfunctions$users=[...];

returnarray_map(function($user){

$user['name']=$user['first'].''.$user['last'];

return$user;

},array_filter($users,function($user){

return$user['status']=='admin';

}));

Here,we’vegottenridofatemporaryvariable(+$admins+)andconvertedoneconfusingforeachloopintotwodistinctactions:mapandfilter.

Theproblemis,PHP’sarraymanipulationfunctionsareawfulandconfusing.Justlookatthisexample;array_map()takestheclosurefirstandthearraysecond,butarray_filter()takesthearrayfirstandtheclosuresecond.Inaddition,ifweaddedanycomplexitytothis,we’dhavefunctionswrappingfunctionswrappingfunctions.It’samess.

Laravel’scollectionstakethepowerofPHP’sarraymanipulationmethodsandgivethemaclean,fluentsyntax—andtheyaddmanymethodsthatdon’tevenexistinPHP’sarraymanipulationtoolbox.Now,usingthecollect()helpermethodthatturnsanarrayintoaLaravelcollection,wecandowhat’sshowninExample17-3:

Example17-3.RefactoringtheforeachloopwithLaravel’scollections$users=collect([...]);

return$users->filter(function($user){

return$user['status']=='admin';

})->map(function($user){

$user['name']=$user['first'].''.$user['last'];

return$user;

});

Thisisn’tthemostextremeofexamples.Thereareplentywherethereductioninlinesofcodeandtheincreasedsimplicitywouldmakeanevenstrongercase.Butthisrighthereisso

Page 674: Laravel: Up and Running: A Framework for Building Modern PHP

common.

Lookattheoriginalexampleandhowmuddyitis.It’snotentirelyclearuntilyouunderstandtheentirecodesamplewhatanygivenpieceistherefor.

Thebiggestbenefitcollectionsprovide,overanythingelse,isbreakingtheactionsyou’retakingtomanipulateanarrayintosimple,discrete,understandabletasks.Youcannowdosomethinglikethis:

$users=[...]

$countAdmins=collect($users)->filter(function($user){

return$user['status']=='admin'

})->count();

Orsomethinglikethis:

$users=[...];

$greenTeamPoints=collect($users)->filter(function($user){

return$user->team=='green';

})->sum('points');

Page 675: Laravel: Up and Running: A Framework for Building Modern PHP

AFewMethodsThere’smuchmoreyoucandothanwhatwe’vecoveredhere.TakealookattheLaravelCollectiondocstolearnmoreaboutallthemethodsyoucanuse,buttogetyoustarted,herearejustafewofthecoremethods:

all()andtoArray()Ifyou’dliketoconvertyourcollectiontoanarray,youcandosowitheitherall()ortoArray().toArray()flattensnotjustthecollection,butalsoanyEloquentobjectsunderneathit,toarrays.all()onlyconvertsthecollectiontoanarray;anyEloquentobjectscontainedwithinthecollectionwillbepreservedasEloquentobjects.Hereareafewexamples:

$users=User::all();

$users->toArray();

/*Returns

[

['id'=>'1','name'=>'Agouhanna'],

...

]

*/

$users->all();

/*Returns

[

EloquentObject{id:1,name:'Agouhanna'},

...

]

*/

filter()andreject()Whenyouwanttogetasubsetofyouroriginalcollectionbycheckingeachitemagainstaclosure,you’llusefilter()(whichkeepsanitemiftheclosurereturnstrue)orreject()(whichkeepsanitemiftheclosurereturnsfalse):

$users=collect([...]);

$admins=$users->filter(function($user){

return$user->isAdmin;

});

$paidUsers=$user->reject(function($user){

return$user->isTrial;

});

where()

where()makesiteasytoprovideasubsetofyouroriginalcollectionwhereagivenkeyisequaltoagivenvalue.Anythingyoucandowithwhere()youcanalsodowithfilter(),butit’sashortcutforacommonscenario:

$users=collect([...]);

Page 676: Laravel: Up and Running: A Framework for Building Modern PHP

$admins=$users->where('role','admin');

first()andlast()Ifyouwantjustasingleitemfromyourcollection,youcanusefirst()topullfromthebeginningofthelistorlast()topullfromtheend.

Ifyoucallfirst()orlast()withnoparameters,they’lljustgiveyouthefirstorlastiteminthecollection.Butifyoupasseitheraclosure,they’llinsteadgiveyouthefirstorlastiteminthecollectionthatreturnstruewhenpassedtothatclosure.

Sometimesyou’lldothisbecauseyouwanttheactualfirstorlastitem.Butsometimesit’stheeasiestwaytogetoneitemevenifyouonlyexpecttheretobeone:

$users=collect([...]);

$owner=$users->first(function($user){

return$user->isOwner;

});

$firstUser=$users->first();

$lastUser=$users->last();

Youcanalsopassasecondparametertoeachmethod,whichisthedefaultvalueandwillbeprovidedasafallbackiftheclosuredoesn’tprovideanyresults.

each()

Ifyou’dliketodosomethingwitheachitemofacollection,butitdoesn’tincludemodifyingtheitemsorthecollectionitself,youcanuseeach():

$users=collect([...]);

$users->each(function($user){

dispatch(newEmailUserAThing($user));

});

map()

Ifyou’dliketoiterateoveralltheitemsinacollection,makechangestothem,andreturnanewcollectionwithallofyourchanges,you’llwanttousemap():

$users=collect([...]);

$users=$users->map(function($user){

return[

'name'=>$user['first'].''.$user['last'],

'email'=>$user['email']

];

});

reduce()

Ifyou’dliketogetasingleresultfromyourcollection,likeacountorastring,you’llprobablywanttousereduce().Youcandefineaninitialvalueforthe“carry,”andaclosurethatacceptsthecurrentstateofthe“carry”andtheneachitemasparameters:

Page 677: Laravel: Up and Running: A Framework for Building Modern PHP

$users=collect([...]);

$points=$users->reduce(function($carry,$user){

return$carry+$user['points']

},0);//Startwithacarryof0

pluck()

Ifyouwanttopulloutjustthevaluesforagivenkeyundereachiteminacollection,youcanusepluck()(formerlylists()):

$users=collect([...]);

$emails=$users->pluck('email')->toArray();

chunk()andtake()chunk()makesiteasytosplityourcollectionintogroupsofapredefinedsize,andtake()pullsjusttheprovidednumberofitems:

$users=collect([...]);

$rowsOfUsers=$users->chunk(3);//Separatesintogroupsof3

$topThree=$users->take(3);//Pullsthefirst3

groupBy()

Ifyouwanttogroupalloftheitemsinyourcollectionbythevalueofoneoftheirproperties,youcanusegroupBy():

$users=collect([...]);

$usersByRole=$users->groupBy('role');

/*Returns:

[

'member'=>[...],

'admin'=>[...]

]

*/

Youcanalsopassaclosure,andwhateveryoureturnfromtheclosurewillbewhat’susedtogrouptherecords:

$heroes=collect([...]);

$heroesByAbilityType=$heroes->groupBy(function($hero){

if($hero->canFly()&&$hero->isInvulnerable()){

return'Kryptonian';

}

if($hero->bitByARadioactiveSpider()){

return'Spidermanesque';

}

Page 678: Laravel: Up and Running: A Framework for Building Modern PHP

if($hero->color==='green'&&$hero->likesSmashing()){

return'Hulk-like';

}

return'Generic';

});

reverse()andshuffle()reverse()reversestheorderoftheitemsinyourcollection,andshuffle()randomizesthem:

$numbers=collect([1,2,3]);

$numbers->reverse()->toArray();//[3,2,1]

$numbers->shuffle()->toArray();//[2,3,1]

sort(),sortBy(),andsortByDesc()Ifyouritemsaresimplestringsorintegers,youcanusesort()tosortthem:

$sortedNumbers=collect([1,7,6,4])->sort()->toArray();//[1,4,6,7]

Ifthey’remorecomplex,youcanpassastring(representingtheproperty)oraclosuretosortBy()orsortByDesc()todefineyoursortingbehavior:

$users=collect([...]);

//Sortanarrayofusersbytheir'email'property

$users->sort('email');

//Sortanarrayofusersbytheir'email'property

$users->sort(function($user,$key){

return$user['email'];

});

count()andisEmpty()Youcanseehowmanyitemsthereareinyourcollectionusingcount()orisEmpty():

$numbers=collect([1,2,3]);

$numbers->count();//3

$numbers->isEmpty();//false

avg()andsum()Ifyou’reworkingwithacollectionofnumbers,avg()andsum()dowhattheirmethodnamessay,anddon’trequireanyparameters:

collect([1,2,3])->sum();//6

collect([1,2,3])->avg();//2

Page 679: Laravel: Up and Running: A Framework for Building Modern PHP

Butifyou’reworkingwitharrays,youcanpassthekeyofthepropertyyou’dliketopullfromeacharraytooperateon:

$users=collect([...]);

$sumPoints=$users->sum('points');

$avgPoints=$users->avg('points');

Page 680: Laravel: Up and Running: A Framework for Building Modern PHP
Page 681: Laravel: Up and Running: A Framework for Building Modern PHP

USINGCOLLECTIONSOUTSIDEOFLARAVELHaveyoufalleninlovewithcollections,anddoyouwanttousethemonyournon-Laravelprojects?WithTaylor’sblessing,IsplitoutjustthecollectionsfunctionalityfromLaravelintoaseparateprojectcalledCollect,anddevelopersatmycompanykeepitup-to-datewithLaravel’sreleases.

Justusethecomposerrequiretightenco/collectcommandandyou’llhavetheIlluminate\Support\Collectionclassreadytouseinyourcode—alongwiththecollect()helper.

Page 682: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DRLaravelprovidesasuiteofglobalhelperfunctionsthatmakeitsimplertodoallsortsoftasks.Theymakeiteasiertomanipulateandinspectarraysandstrings,theymakeiteasiertogeneratepathsandURLs,andtheyprovidesimpleaccesstosomeconsistentandvitalfunctionality.

Laravel’scollectionsarepowerfultoolsthatbringthepossibilityofcollectionpipelinestoPHP.

Page 683: Laravel: Up and Running: A Framework for Building Modern PHP

Glossary

AccessorAmethoddefinedonanEloquentmodelthatcustomizeshowagivenpropertywillbereturned.Accessorsmakeitpossibletodefinethatgettingagivenpropertyfromamodelwillreturnadifferent(or,morelikely,differentlyformatted)valuethanwhatisstoredinthedatabaseforthatproperty.

ActiveRecordAcommondatabaseORMpattern,andalsothepatternthatLaravel’sEloquentuses.InActiveRecordthesamemodelclassdefinesbothhowtoretrieveandpersistdatabaserecordsandhowtorepresentthem.Additionally,eachdatabaserecordisrepresentedbyasingleentityintheapplication,andeachentityintheapplicationismappedtoasingledatabaserecord.

ApplicationtestOftencalledacceptanceorfunctionaltests,applicationteststesttheentirebehavioroftheapplication,usuallyatanouterboundary,byemployingsomethinglikeaDOMcrawler—whichisexactlywhatLaravel’sapplicationtestsuiteoffers.

Argument(Artisan)ArgumentsareparametersthatcanbepassedtoArtisanconsolecommands.Argumentsaren’tprefacedwith--orfollowedby=,butinsteadjustacceptasinglevalue.

ArtisanThetoolthatmakesitpossibletointeractwithLaravelapplicationsfromthecommandline.

AssertionIntesting,anassertionisthecoreofthetest:youareassertingthatsomethingshouldbeequalto(orlessthanorgreaterthan)somethingelse,orthatitshouldhaveagivencount,orwhateverelseyoulike.Assertionsarethethingsthatcaneitherpassorfail.

AuthenticationCorrectlyidentifyingoneselfasamember/userofanapplicationistheactofauthentication.Authenticationdoesn’tdefinewhatyoumaydo,butsimplywhoyouare(oraren’t).

Authorization

Page 684: Laravel: Up and Running: A Framework for Building Modern PHP

Assumingyou’veeithersucceededorfailedatauthenticatingyourself,authorizationdefineswhatyou’reallowedtodogivenyourparticularidentification.Authorizationisaboutaccessandcontrol.

AutowiringWhenadependencyinjectioncontainerwillinjectaninstanceofaresolvableclasswithoutadeveloperhavingexplicitlytaughtithowtoresolvethatclass,that’scalledautowiring.Withacontainerthatdoesn’thaveautowiring,youcan’teveninjectaplainPHPobjectwithnodependenciesuntilyouhaveexplicitlyboundittothecontainer.Withautowiring,youonlyhavetoexplicitlybindsomethingtothecontainerifitsdependenciesaretoocomplexorvagueforthecontainertofigureoutonitsown.

beanstalkdBeanstalkisaworkqueue.It’ssimpleandexcelsatrunningmultipleasynchronoustasks—whichmakesitacommondriverforLaravel’squeues.beanstalkdisitsdaemon.

BladeLaravel’stemplatingengine.

CarbonAPHPpackagethatmakesworkingwithdatesmucheasierandmoreexpressive.

CashierALaravelpackagethatmakesbillingwithStripeorBraintree,especiallyinsubscriptioncontexts,easierandmoreconsistentandpowerful.

ClosureClosuresarePHP’sversionofanonymousfunctions.Aclosureisafunctionthatyoucanpassaroundasanobject,assigntoavariable,passasaparametertootherfunctionsandmethods,orevenserialize.

CodeIgniterAnolderPHPframeworkthatLaravelwasinspiredby.

CollectionThenameofadevelopmentpatternandalsoLaravel’stoolthatimplementsit.Likearraysonsteroids,collectionsprovidemap,reduce,filter,andmanymorepowerfuloperationsthatPHP’snativearraysdon’t.

CommandThenameforacustomArtisanconsoletask.

ComposerPHP’sdependencymanager.LikeRubyGemsorNPM.

Page 685: Laravel: Up and Running: A Framework for Building Modern PHP

ContainerSomewhatofacatchallword,inLaravel“container”referstotheapplicationcontainerthat’sresponsiblefordependencyinjection.Accessibleviaapp()andalsoresponsibleforresolvingcallstocontrollers,events,jobs,andcommands,thecontaineristhegluethatholdseachLaravelapptogether.

ContractAnothernameforaninterface.

ControllerAclassthatisresponsibleforroutinguserrequeststhroughtotheapplication’sservicesanddata,andreturningsomeformofusefulresponsebacktotheuser.

CSRF(cross-siterequestforgery)Amaliciousattackwhereanexternalsitemakesrequestsagainstyourapplicationbyhijackingyourusers’browsers(withJavaScript,likely)whilethey’restillloggedintoyoursite.Protectedagainstbyaddingatoken(andacheckforthattokenonthePOSTside)toeveryformonthesite.

DependencyinjectionInsteadofinstantiatingdependenciesinaclass,expectthemtobeinjectedinfromtheoutside—usuallythroughtheconstructor.

DirectiveBladesyntaxoptionslike@if,@unless,etc.

DotnotationNavigatingdowninheritancetreesusing.toreferenceajumpdowntoanewlevel.Ifyouhaveanarraylike:['owner'=>['address'=>['line1'=>'123MainSt.']]],youhavethreelevelsofnesting.Usingdotnotation,youwouldrepresent“123MainSt.”as"owner.address.line1".

EagerloadingAvoidingN+1problemsbyaddingasecondsmartquerytoyourfirstquerytogetasetofrelateditems.UsuallyyouhaveafirstquerythatgetsacollectionofthingA.ButeachAhasmanyB,andsoeverytimeyougettheBfromanA,youneedanewquery.Eagerloadingmeansdoingtwoqueries:firstyougetalltheA’s,andsecondyougetalltheB’srelatedtoallthoseA’s,inasinglequery.Twoqueries,andyou’redone.

EchoALaravelproductthatmakesWebSocketauthenticationandsyncingofdatasimple.

Elixir

Page 686: Laravel: Up and Running: A Framework for Building Modern PHP

Laravel’sbuildtool;awrapperaroundGulp.

EloquentLaravel’sActiveRecordORM.Thetoolyou’llusetodefinesomethinglikeaUsermodel.

EnvironmentvariableVariablesthataredefinedinan.envfilethatisexpectedtobeexcludedfromversioncontrol.Thismeanstheydon’tsyncbetweenenvironmentsandthey’realsokeptsafe.

EnvoyerALaravelproductforzero-down-timedeployment.

EventLaravel’stoolforimplementingapub/suborobserverpattern.Eacheventrepresentsthataneventhappened:thenameoftheeventdescribeswhathappened(e.g.,UserSubscribed)andthepayloadallowsforattachingrelevantinformation.Designedtobe“fired”andthen“listened”for(orpublishedandsubscribed,ifyoupreferthepub/subconcept).

FacadeAtoolinLaravelforsimplifyingaccesstocomplextools.FacadesprovidestaticaccesstocoreservicesinLaravel.Sinceeveryfacadeisbackedbyaclassinthecontainer,youcouldreplaceanycalltosomethinglikeCache::put();withatwo-linecalltosomethinglike$cache=app('cache');$cache->put();.

FlagAparameteranywherethatisonoroff(boolean).

FluentMethodsthatcanbechainedoneafteranotheraresaidtobefluent.Inordertoprovideafluentsyntax,eachmethodmustreturntheinstance,preparingittobechainedagain.ThisallowsforsomethinglikePeople::where('age','>',14)->orderBy('name')->get().

FlysystemThepackagethatLaravelusestofacilitateitslocalandcloudfileaccess.

ForgeALaravelproductthatmakesiteasytospinupandmanagevirtualserversonmajorcloudproviderslikeDigitalOceanandAWS.

FQCN(fully-qualifiedclassname)Thefullnamespacednameofanygivenclass,trait,orinterface.Controlleristheclassname;Illuminate\Routing\ControlleristheFQCN.

Gulp

Page 687: Laravel: Up and Running: A Framework for Building Modern PHP

AJavaScript-basedbuildtool.

HelperAgloballyaccessiblePHPfunctionthatmakessomeotherfunctionalityeasier—forexample,array_get()simplifiesthelogicoflookingupresultsfromarrays.

HomesteadALaraveltoolthatwrapsVagrantandmakesiteasiertospinupForge-parallelvirtualserversforlocalLaraveldevelopment.

IlluminateThetop-levelnamespaceofallLaravelcomponents.

IntegrationtestIntegrationteststestthewayindividualunitsworktogetherandpassmessages.

IoC(inversionofcontrol)Theconceptofgiving“control”overhowtomakeaconcreteinstanceofaninterfacetothehigher-levelcodeofthepackageinsteadofthelower-levelcode.WithoutIoC,eachindividualcontrollerandclassmightdecidewhatinstanceofMaileritwantedtocreate.IoCmakesitsothatthelow-levelcode—thosecontrollersandclasses—justgettoaskforaMailer,andsomehigh-levelconfigurationcodedefinesonceperapplicationwhichinstanceshouldbeprovidedtosatisfythatrequest.

JobAclassthatintendstoencapsulateasingletask.Jobsareintendedtobeabletobepushedontoaqueueandrunasynchronously.

JSONJavaScriptObjectNotation.Asyntaxfordatarepresentation.

JWT(JSONWebToken)AJSONobjectcontainingalloftheinformationnecessarytodetermineauser ’sauthenticationstateandaccesspermissions.ThisJSONobjectisdigitallysigned,whichiswhatmakesittrustworthy,usingHMACorRSA.Usuallydeliveredintheheader.

MassassignmentTheabilitytopassmanyparametersatoncetocreateorupdateanEloquentmodel,usingakeyedarray.

MiddlewareAseriesofwrappersaroundanapplicationthatfilteranddecorateitsinputsandoutputs.

Memcached

Page 688: Laravel: Up and Running: A Framework for Building Modern PHP

Anin-memorydatastoredesignedtoprovidesimplebutfastdatastorage.Memcachedonlysupportsabasickey/valuestore.

MigrationAmanipulationtothestateofthedatabase,storedinandrunfromcode.

MockeryAlibraryincludedwithLaravelthatmakesiteasytomockPHPclassesinyourtests.

ModelfactoryAtoolfordefininghowtheapplicationcangenerateaninstanceofyourmodelifneededfortestingorseeding.UsuallypairedwithafakedatageneratorlikeFaker.

MultitenancyAsingleappservingmultipleclients,eachofwhichhasitscustomers.Multitenancyoftensuggeststhateachclientofyourapplicationgetsitsowntheminganddomainname,withwhichtodifferentiateitsservicetoitscustomersvis-à-visyourotherclients’potentialservices.

MutatorAtoolinEloquentthatallowsyoutomanipulatethedatabeingsavedtoamodelpropertybeforeitissavedtothedatabase.

NginxAwebserversimilartoApache.

Option(Artisan)Likearguments,optionsareparametersthatcanbepassedtoArtisancommands.They’reprefacedwith--andcanbeusedasaflag(--force)ortoprovidedata(--userId=5).

ORM(object-relationalmapper)Adesignpatternthatiscenteredaroundusingobjectsinaprogramminglanguagetorepresentdata,anditsrelationships,inarelationaldatabase.

PassportALaravelpackagethatcanbeusedtoeasilyaddanOAuthauthenticationservertoyourLaravelapp.

PHPSpecAPHPtestingframework.

PHPUnitAPHPtestingframework.ThemostcommonandconnectedtothemostofLaravel’scustomtestingcode.

Page 689: Laravel: Up and Running: A Framework for Building Modern PHP

PolymorphicIndatabaseterms,abletointeractwithmultipledatabasetableswithsimilarcharacteristics.Apolymorphicrelationshipwillallowentitiesofmultiplemodelstobeattachedinthesameway.

PreprocessorAbuildtoolthattakesinaspecialformofalanguage(forCSS,onespecialformisLESS)andgeneratescodewithjustthenormallanguage(CSS).Preprocessorsbuildintoolsandfeaturesthatarenotinthecorelanguage.

PrimarykeyMostdatabasetableshaveasinglecolumnthatisintendedtorepresenteachrow.Thisiscalledtheprimarykeyandiscommonlynamedid.

QueueAstackontowhichjobscanbeadded.Usuallyassociatedwithaqueueworker,whichpullsjobsoneatatimefromaqueue,worksonthem,andthendiscardsthem.

RedisLikeMemcached,adatastoresimplerthanmostrelationaldatabasesbutpowerfulandfast.Redissupportsaverylimitedsetofstructuresanddatatypesbutmakesupforitinspeedandscalability.

RESTRepresentationalStateTransfer,themostcommonformatforAPIsthesedays.UsuallysuggeststhatinteractionswithanAPIshouldeachauthenticateseparatelyandshouldbe“stateless”;alsousuallysuggeststhattheyusetheHTTPverbsforbasicdifferentiationofrequests.

RouteAdefinitionofawayorwaystheusermightvisitawebapplication.Arouteisapatterndefinition;itcanbesomethinglike/users/5,or/users,or/users/{id}.

SaaSSoftwareasaService.Web-basedapplicationsthatyoupaymoneytouse.

ScopeInEloquent,atoolfordefininghowtoconsistentlyandsimplynarrowdownaquery.

ScoutALaravelpackageforfull-textsearchonEloquentmodels.

Serialization

Page 690: Laravel: Up and Running: A Framework for Building Modern PHP

Theprocessofconvertingmorecomplexdata(usuallyanEloquentmodel)tosomethingsimpler(inLaravel,usuallyanarrayorJSON).

ServiceproviderAstructureinLaravelthatregistersandbootsclassesandcontainerbindings.

SoftdeleteMarkingadatabaserowas“deleted”withoutactuallydeletingit,usuallypairedwithanORMthatbydefaulthidesall“deleted”rows.

SparkALaraveltoolthatmakesiteasytospinupanewsubscription-basedSaaSapp.

SymfonyAPHPframeworkthatfocusesonbuildingexcellentcomponentsandmakingthemaccessibletoothers.Symfony’sHTTPFoundationisatthecoreofLaravelandeveryothermodernPHPframework.

TinkerLaravel’sREPL,orread–evaluate–printloop.It’satoolthatallowsyoutoperformcomplexPHPoperationswithinthefullcontextofyourappfromthecommandline.

TL;DRToolong;didn’tread.“Summary.”

TypehintPrefacingavariablenameinamethodsignaturewithaclassorinterfacename.TellsPHP(andLaravel,andotherdevelopers)thattheonlythingthat’sallowedtobepassedinthatparameterisanobjectwiththegivenclassorinterface.

UnittestUnitteststargetsmall,relativelyisolatedunits—aclassormethod,usually.

VagrantAcommand-linetoolthatmakesiteasytobuildvirtualmachinesonyourlocalcomputerusingpredefinedimages.

ValetALaravelpackage(forMacOSusers,butthereareforksforLinuxandWindows)thatmakesiteasytoserveyourapplicationsfromyourdevelopmentfolderofchoice,withoutworryingaboutVagrantorvirtualmachines.

ValidationEnsuringthatuserinputmatchesexpectedpatterns.

Page 691: Laravel: Up and Running: A Framework for Building Modern PHP

ViewcomposerAtoolthatdefinesthat,everytimeagivenviewisloaded,itwillbeprovidedacertainsetofdata.

ViewAtemplatefilethatdefinesHTMLtobesenttotheenduser;oftenincludesacceptingdatafromacontrollerandformattingitaspartoftheHTML.

Page 692: Laravel: Up and Running: A Framework for Building Modern PHP

Index

Symbols

arrow

->chainingmethods,RouteNames,Middleware

->traversingJSONstructure,JSONoperations

=>precedingTinkerresponses,Tinker

*(asterisk),followingarrayargumentsoroptions,Arrayargumentsandarrayoptions

@(atsign)

incontroller/methodreference,Auth::routes()

precedingBladedirectives,BladeTemplating

precedingBladeechosyntax,EchoingData

\(backslash),escapinginArtisancommands,ReceivingtheMessage

{}(braces)

enclosingArtisancommandarguments,Arguments,required,optional,and/orwithdefaults

enclosingrouteparameters,RouteModelBinding,FromRouteParameters

{{}},Bladeechosyntax,escaped,EchoingData,{{Versus{!!

{!!!!},Bladeechosyntax,notescaped,EchoingData,{{Versus{!!

::(colon,double),infacades,FacadesandtheContainer

=(equalsign),inArtisanargumentdefinition,Arguments,required,optional,and/orwithdefaults

--(hyphen,double),precedingArtisancommandoptions,Options,requiredvalues,value

Page 693: Laravel: Up and Running: A Framework for Building Modern PHP

defaults,andshortcuts

.(period),dotnotation,Glossary

?(questionmark)

followingoptionalArtisancommandarguments,Arguments,required,optional,and/orwithdefaults

followingoptionalparameters,RouteParameters

queryparameters,Parameterbindingsandnamedbindings

/(slash),escapinginArtisancommands,ControllerOrganizationandJSONReturns

A

abilities(rules)forauthorization,DefiningAuthorizationRules

abort()helper,AbortingtheRequest,Misc

abort_if()helper,AbortingtheRequest,Misc

abort_unless()helper,AbortingtheRequest,Misc

acceptancetests(seeapplicationtests)

accepts()method,Request,Userandrequeststate

accesscontrollist(ACL)(seeauthorization)

accessors,Accessors,HidingattributesfromJSON,Testing,Glossary

ACL(accesscontrollist)(seeauthorization)

action()helper,OtherRedirectMethods,URLs

ActiveRecordpattern,IntroductiontoEloquent,Glossary

(seealsoEloquent)

add()method,Cache,TheMethodsAvailableonCacheInstances

addGlobalScope()method,Globalscopes

Page 694: Laravel: Up and Running: A Framework for Building Modern PHP

after()method,Blueprint,Buildingextrapropertiesfluently

after()method,tasks,TaskHooks

AlgoliaSDK,InstallingScout

aliases,bindingto,BindingtoSingletons,Aliases,andInstances

all()method,collection,AFewMethods

all()method,Eloquent,RetrievingDatawithEloquent,Getmany

all()method,ParameterBag,Basicuserinput

all()method,Request,$request->all(),EloquentModelMassAssignment,Basicuserinput

all()method,Session,TheMethodsAvailableonSessionInstances

allDirectories()method,Storage,UsingtheStorageFacade

allFiles()method,Storage,UsingtheStorageFacade

allows()method,Gate,TheGateFacade(andInjectingGate)

anonymousfunctions(seeclosures)

anticipate()method,Prompts

apiguard,Guards

apimiddlewaregroup,Usingmiddlewaregroups

APIroutes,RouteDefinitions

(seealsoroutes)

api.phpfile,RouteDefinitions

APIs,WritingAPIs-TheBasicsofREST-LikeJSONAPIs

authenticationwithAPItokens,Laravel5.2+APITokenAuthentication-Laravel5.2+APITokenAuthentication

Page 695: Laravel: Up and Running: A Framework for Building Modern PHP

authenticationwithPassport,APIAuthenticationwithLaravelPassport-PassportScopes

filteringresults,FilteringYourAPIResults

JSONfor,TheBasicsofREST-LikeJSONAPIs,ControllerOrganizationandJSONReturns,SortingandFiltering

nestingrelationshipsbetweenresources,NestingandRelationships-NestingandRelationships

paginatingresults,EloquentPagination-EloquentPagination

requestheaders,reading,ReadingandSendingHeaders,ReadingRequestHeadersinLaravel

resourcecontrollers,ControllerOrganizationandJSONReturns-ControllerOrganizationandJSONReturns

responseheaders,sending,ReadingandSendingHeaders

RESTstyleof,TheBasicsofREST-LikeJSONAPIs-TheBasicsofREST-LikeJSONAPIs

sortingresults,SortingandFiltering-SortingYourAPIResults

testing,Testing

transformingresults,TransformingResults-WritingYourOwnTransformer

.appdomain,ConfiguringHomestead

appfolder,TheFolders

app()helper,GettingaRequestObjectinLaravel,Theapp()GlobalHelper

app-namespacedcommands,Artisan,TheGroupedCommands

app.jsfile,BringingEchointoyourproject

app.phpfileinconfig,InstallingScout,Thebroadcastserviceprovider

Page 696: Laravel: Up and Running: A Framework for Building Modern PHP

append()method,Storage,UsingtheStorageFacade

appendOutputTo()method,tasks,HandlingTaskOutput

application

bootstrapping,BootstrappingtheApplication-ServiceProviders

exiting,Misc

kernel,Laravel’skernel

lifecycle,Laravel’sRequestLifecycle-ServiceProviders

applicationcontainer(seecontainer)

applicationtests,Testing,ApplicationTesting-AuthenticationandSessions,Glossary

assertions,CustomApplicationTestingAssertions-JSONandNon-visit()ApplicationTestingAssertions,Glossary

clickingandforms,ClickingandForms-ClickingandForms

jobsandevents,JobsandEvents

visitingroutes,“Visiting”Routes-“Visiting”Routes

AppServiceProvider,ServiceProviders

app_path()helper,ApplicationPaths

argument()method,Artisan,argument()

arguments(Artisan),Glossary

arrays

asArtisanargumentsoroptions,Arrayargumentsandarrayoptions

collectionsasalternativeto,Introducingthebasecollection

convertingtocollections,Misc

Page 697: Laravel: Up and Running: A Framework for Building Modern PHP

helpersfor,Arrays-Arrays

array_filter()method,TheBasicsofCollections

array_first()helper,Arrays

array_get()helper,Arrays

array_has()helper,Arrays

array_map()method,TheBasicsofCollections

array_pluck()helper,Arrays

arrow

->chainingmethods,RouteNames,Middleware

->traversingJSONstructure,JSONoperations

=>precedingTinkerresponses,Tinker

Artisancommands,AnIntroductiontoArtisan-CallingArtisanCommandsinNormalCode,Glossary,Glossary

argumentsfor,Glossary

callingfromcode,ASampleCommand,CallingArtisanCommandsinNormalCode

callingothercommandsfrom,CallingArtisanCommandsinNormalCode

custom,WritingCustomArtisanCommands-Inputdescriptions

escapingslashesin,ControllerOrganizationandJSONReturns

listing,AnIntroductiontoArtisan

optionsfor,Options-Options,Glossary

outputduring,Output-Progressbars

progressbarsfor,Progressbars

Page 698: Laravel: Up and Running: A Framework for Building Modern PHP

promptingforuserinput,Prompts-Prompts

queueing,TheGroupedCommands,QueuesSupportingOtherFunctions

schedulingastasks,AvailableTaskTypes

testing,Testing,ArtisanandSeed

usinginputfrom,UsingInput-Prompts

Artisanfacade,CallingArtisanCommandsinNormalCode,Testing

artisanfile,TheLooseFiles

artisan()method,TestCase,ArtisanandSeed

ask()method,Prompts

assertEquals()method,TestCase,Cache

assertHasOldInput()method,TestCase,Session

assertions,CustomApplicationTestingAssertions-JSONandNon-visit()ApplicationTestingAssertions,Glossary

assertNotSent()method,notification,Notifications

assertPageLoaded()method,TestCase,CustomApplicationTestingAssertions

assertRedirectedTo()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

assertRedirectedToAction()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

assertRedirectedToRoute()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

assertResponseOK()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

Page 699: Laravel: Up and Running: A Framework for Building Modern PHP

assertResponseStatus()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

assertSent()method,notification,Notifications

assertSessionHas()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions,Session

assertSessionHasAll()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions,Session

assertSessionHasErrors()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions,Session

assertSessionMissing()method,TestCase,Session

assertViewHas()method,TestCase,Testing,JSONandNon-visit()ApplicationTestingAssertions

assertViewHasAll()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

assertViewMissing()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

assetsfolder,ElixirFolderStructure

associate()method,Eloquent,Onetomany

asterisk(*),followingarrayargumentsoroptions,Arrayargumentsandarrayoptions

atsign(@)

incontroller/methodreference,Auth::routes()

precedingBladedirectives,BladeTemplating

precedingBladeechosyntax,EchoingData

attach()method,Eloquent,Manytomany

Page 700: Laravel: Up and Running: A Framework for Building Modern PHP

attach()method,mailable,MethodsAvailableinbuild()

attach()method,TestCase,ClickingandForms

attachData()method,mailable,MethodsAvailableinbuild()

attempt()method,authentication,“RememberMe”

attempts()method,jobs,Limitingthenumberoftries

attributecasting,Attributecasting

authcommands,Artisan,TheGroupedCommands

Authfacade,Usingtheauth()GlobalHelperandtheAuthFacade

authmiddleware,AuthMiddleware

authscaffold,TheAuthScaffold-TheAuthScaffold

auth()helper,Usingtheauth()GlobalHelperandtheAuthFacade,Misc

auth.basicmiddleware,AuthMiddleware

auth.phpfile,ChangingtheDefaultGuard,InstallingPassport

Auth::routes()facade,Auth::routes()

AuthController,UserAuthenticationandAuthorization

Authenticatablecontract,TheUserModelandMigration

authentication,UserAuthenticationandAuthorization-AuthEvents,Glossary

APIsfor,APIAuthenticationwithLaravelPassport-Laravel5.2+APITokenAuthentication

contracts,TheUserModelandMigration

create_users_tablemigration,TheUserModelandMigration

events,AuthEvents

Page 701: Laravel: Up and Running: A Framework for Building Modern PHP

ForgotPasswordController,ForgotPasswordController

guardsfor,Guards-CustomUserProvidersforNonrelationalDatabases

LoginController,LoginController-ThrottlesLoginstrait

manualauthentication,ManuallyAuthenticatingUsers

RegisterController,RegisterController-RegistersUserstrait

RegistersUserstrait,RegistersUserstrait

remembermeaccesstoken,“RememberMe”

ResetPasswordController,ResetPasswordController

routemiddlewarefor,AuthMiddleware

routesfor,Auth::routes()

settingup,TheAuthScaffold

testing,Testing-Testing,AuthenticationandSessions

Usermodel,TheUserModelandMigration-TheUserModelandMigration

viewsfor,TheAuthScaffold-TheAuthScaffold

WebSocket(seeEcho)

Authorizablecontract,TheUserModelandMigration

Authorizabletrait,CheckingontheUserInstance

authorization,TheUserModelandMigration,Authorization(ACL)andRoles-Overridingpolicies,Glossary

Authorizablecontract,TheUserModelandMigration

AuthorizesRequeststrait,ControllerAuthorization-CheckingontheUserInstance

Bladechecks,BladeChecks

Page 702: Laravel: Up and Running: A Framework for Building Modern PHP

checkingusercapabilities,CheckingontheUserInstance-InterceptingChecks

Gatefacade,Authorization(ACL)andRoles,TheGateFacade(andInjectingGate)

overridingchecks,InterceptingChecks

Passportpackage,Overridingpolicies

policies,Policies-Overridingpolicies

routemiddlewarefor,TheAuthorizeMiddleware

rules(abilities)for,defining,DefiningAuthorizationRules

testing,Testing-Testing

authorizationcodegrant,Passport,Authorizationcodegrant-Authorizationcodegrant

authorize()method,AuthorizesRequeststrait,ControllerAuthorization

authorize()method,formrequest,CreatingaFormRequest

authorizeForUser()method,AuthorizesRequeststrait,ControllerAuthorization

authorizeResource()method,AuthorizesRequeststrait,ControllerAuthorization

AuthorizesRequeststrait,ControllerAuthorization-CheckingontheUserInstance

AuthServiceProvider,DefiningAuthorizationRules,Generatingpolicies,ServiceProviders,PassportScopes

autowiring,HowtheContainerIsWired-HowtheContainerIsWired,Glossary

avg()method,collection,AFewMethods

avg()method,DB,Ending/returningmethods

avg()method,Eloquent,Aggregates

away()method,redirects,OtherRedirectMethods

B

Page 703: Laravel: Up and Running: A Framework for Building Modern PHP

back()helper,redirect()->back(),Redirectresponses,Misc

backslash(\),escapinginArtisancommands,ReceivingtheMessage

base_path()helper,ApplicationPaths

be()method,TestCase,Testing,AuthenticationandSessions

beanstalkdqueues,BasicQueueConfiguration,Glossary

before()method,tasks,TaskHooks

beginTransaction()method,DB,Transactions

Behat,Testing

belongsTo()method,Eloquent,Onetoone,Onetomany,ChildRecordsUpdatingParentRecordTimestamps

belongsToMany()method,Eloquent,Manytomany,ChildRecordsUpdatingParentRecordTimestamps

bigIncrements()method,Blueprint,Creatingcolumns

bigInteger()method,Blueprint,Creatingcolumns

billing(seeCashierpackage)

binary()method,Blueprint,Creatingcolumns

bind()method,BindingtoaClosure,BindingtoSingletons,Aliases,andInstances

binding

classestocontainer,BindingClassestotheContainer-ContextualBinding

datatoviews,BindingDatatoViewsUsingViewComposers-Class-basedviewcomposers

PDOparameterbinding,Parameterbindingsandnamedbindings

routemodelbinding,RouteModelBinding-CustomRouteModelBinding

Page 704: Laravel: Up and Running: A Framework for Building Modern PHP

Blade,BladeTemplating-BladeTemplating,Glossary

checksusing,BladeChecks

conditionals,Conditionals

customdirectives,CustomBladeDirectives-Example:UsingCustomBladeDirectivesforaMultitenantApp

directivesfor,BladeTemplating,Glossary

echoingPHPin,EchoingData

$expressionparameter,ParametersinCustomBladeDirectives

includedviewpartials,@include,@each

loops,Loops-@forelse,@each

multitenancyusing,Example:UsingCustomBladeDirectivesforaMultitenantApp-Example:UsingCustomBladeDirectivesforaMultitenantApp

orhelper,or

sections,DefiningSectionswith@section/@showand@yield

serviceinjection,BladeServiceInjection-BladeServiceInjection

templates,Views,TemplateInheritance-@each

Blueprintclass,Creatingcolumns-Buildingextrapropertiesfluently

boolean()method,Blueprint,Creatingcolumns

boot()method,Eloquentmodel,Globalscopes

boot()method,serviceproviders,CustomRouteModelBinding,DefiningAuthorizationRules,ServiceProviders,PassportScopes

bootstrapfolder,TheFolders

bootstrappingapplication,BootstrappingtheApplication-ServiceProviders

Page 705: Laravel: Up and Running: A Framework for Building Modern PHP

braces({})

enclosingArtisancommandarguments,Arguments,required,optional,and/orwithdefaults

enclosingrouteparameters,RouteModelBinding,FromRouteParameters

{{}},Bladeechosyntax,escaped,EchoingData,{{Versus{!!

{!!!!},Bladeechosyntax,notescaped,EchoingData,{{Versus{!!

broadcastnotifications,Broadcastnotifications

broadcast()helper,Excludingthecurrentuserfrombroadcastevents,Excludingthecurrentuser

broadcastAs()method,events,BroadcastinganEvent

broadcastingevents(seeWebSockets)

broadcasting.phpfile,ConfigurationandSetup

broadcastOn()method,events,FiringanEvent,BroadcastinganEvent

BroadcastServiceProvider,Thebroadcastserviceprovider

broadcastWith()method,events,BroadcastinganEvent

build()method,mailable,Basic“Mailable”MailUsage

C

cachecommands,Artisan,TheGroupedCommands

Cachefacade,AccessingtheCache

cache()helper,AccessingtheCache

cache.phpfile,Cache

caches

accessing,TheGroupedCommands,Cache-TheMethodsAvailableonCacheInstances

Page 706: Laravel: Up and Running: A Framework for Building Modern PHP

datastoresusedby,OtherDatabaseConfigurationOptions

forcustomdirectiveresults,CustomBladeDirectives

forroutes,RouteCaching

testing,Cache

call()method,Artisan,CallingArtisanCommandsinNormalCode

call()method,container,MethodInjection

call()method,TestCase,“Visiting”Routes,Cookies

@candirective,BladeChecks

can()method,Authorizable,CheckingontheUserInstance

@cannotdirective,BladeChecks

cannot()method,Authorizable,CheckingontheUserInstance

CanResetPasswordcontract,TheUserModelandMigration

cant()method,Authorizable,CheckingontheUserInstance

capture()method,Request,TheRequestObject

Carbonpackage,Attributecasting,TheMethodsAvailableonCacheInstances,Glossary

Cashierpackage,Glossary

chainingmethods,RouteNames,Middleware

change()method,Modifyingcolumns

channel()method,Broadcast,BindingauthorizationdefinitionsforWebSocketchannels

channel()method,Echo,UsingEchoforbasiceventbroadcasting

char()method,Blueprint,Creatingcolumns

Page 707: Laravel: Up and Running: A Framework for Building Modern PHP

check()method,authorization,Usingtheauth()GlobalHelperandtheAuthFacade

check()method,TestCase,ClickingandForms

choice()method,Prompts

chunk()method,collection,AFewMethods

chunk()method,Eloquent,Chunkingresponseswithchunk()

classes

autowiringtocontainer,Glossary

FQCN(fully-qualifiedclassname)for,Glossary

viewcomposersusing,Class-basedviewcomposers-Class-basedviewcomposers

clearcommand,Artisan,BasicArtisanCommands

clearInputs()method,TestCase,ClickingandForms

click()method,TestCase,ClickingandForms

closures,RouteDefinitions,Glossary

bindingto,BindingtoaClosure

definingArtisancommandsas,RegisteringCommands

definingroutegroupsusing,RouteGroups

definingroutesusing,RouteDefinitions-RouteDefinitions,RouteHandling

viewcomposersusing,Closure-basedviewcomposers

Cloudstorage(seestorage)

cloud-basedmail,Mail

CodeIgniterframework,Glossary

collect()helper,Introducingthebasecollection,Misc,TheBasicsofCollections

Page 708: Laravel: Up and Running: A Framework for Building Modern PHP

Collectionclass,Rawselects,Introducingthebasecollection-Introducingthebasecollection

Collectionpattern,Glossary

collections,Collections-AFewMethods

comparedtoarrays,TheBasicsofCollections-TheBasicsofCollections

convertingtoarrays,AFewMethods

returnedbyEloquent,EloquentCollections-WhatEloquentcollectionsadd

serialization,EloquentSerialization

usingoutsideLaravel,AFewMethods

colon,double(::),infacades,FacadesandtheContainer

commands,Artisan(seeArtisancommands)

comment()method,Artisan,Output

commit()method,DB,Transactions

compiled.phpfile,BootstrappingtheApplication

Composer,Composer,BootstrappingtheApplication,Glossary

commandsfor,InstallingLaravelwiththeLaravelInstallerTool,InstallingLaravelwithComposer’screate-projectFeature

newprojects,creating,InstallingLaravelwithComposer’screate-projectFeature

serviceproviderfeatureswith,ServiceProviders

composer.jsonfile,TheLooseFiles

composer.lockfile,TheLooseFiles

conditionals(Blade),Conditionals

configcommands,Artisan,TheGroupedCommands

Page 709: Laravel: Up and Running: A Framework for Building Modern PHP

configfolder,TheFolders,Configuration

config()helper,Misc

config/app.phpfile,InstallingScout,Thebroadcastserviceprovider

configurationfiles,TheFolders,Configuration,ChangingtheDefaultGuard,ApplicationPaths

config_path()helper,ApplicationPaths

confirm()method,Artisan,Prompts

Consolecomponent,Symfony,AnIntroductiontoArtisan

constructorinjection,AQuickIntroductiontoDependencyInjection,DependencyInjectionandLaravel,ConstructorInjection

contactinformationforthisbook,HowtoContactUs

container,InjectingDependenciesintoControllers,Glossary

accessingfacadebackingclassfrom,HowFacadesWork

accessingobjectsfrom,Theapp()GlobalHelper

autowiring,HowtheContainerIsWired-HowtheContainerIsWired

bindingclassesto,BindingClassestotheContainer-ContextualBinding

classesin,autowiring,Glossary

constructorinjection,ConstructorInjection

dependencyinjection,AQuickIntroductiontoDependencyInjection-DependencyInjectionandLaravel

methodinjection,MethodInjection

registeringbindingsfor,ServiceProviders

contextualbinding,ContextualBinding

Page 710: Laravel: Up and Running: A Framework for Building Modern PHP

contracts(seeinterfaces)

Contractsnamespace,TheUserModelandMigration

controllers,Controllers-Bindingaresourcecontroller,Glossary

applyingmiddlewareusing,Middleware

creating,Controllers-Controllers

gettingandhandlinguserinput,GettingUserInput-GettingUserInput

handlingroutesusing,RouteHandling

injectingdependenciesinto,InjectingDependenciesintoControllers-InjectingDependenciesintoControllers

methodreferencesyntaxfor,Auth::routes()

namespacefor,Controllers

resourcecontrollers,ResourceControllers-Bindingaresourcecontroller,ControllerOrganizationandJSONReturns-ControllerOrganizationandJSONReturns

Cookiefacade,TheCookiefacade-TheCookiefacade

cookie()helper,Thecookie()globalhelper

cookie()method,Request,Persistence,Readingcookiesfromrequestobjects

cookie()method,Response,Settingcookiesonresponseobjects

CookieJarclass,CookiesinLaravel,TheCookiefacade,Thecookie()globalhelper

cookies,Cookies-Settingcookiesonresponseobjects

accessingwithCookiefacade,TheCookiefacade-TheCookiefacade

accessingwithcookie()helper,Thecookie()globalhelper

accessingwithRequestandResponse,Cookiesonrequestandresponseobjects

configuring,TheCookiefacade

Page 711: Laravel: Up and Running: A Framework for Building Modern PHP

disablingencryptionfor,Cookies

locationsof,CookiesinLaravel

manuallyencryptingfor,Cookies

testing,Cookies-Cookies

copy()method,Storage,UsingtheStorageFacade

count()method,collection,AFewMethods

count()method,DB,Ending/returningmethods

count()method,Eloquent,Aggregates

count()method,ParameterBag,Basicuserinput

create()method,modelfactories,Usingamodelfactory,Inserts,Massassignment

create()method,resourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

create()method,Schema,Creatingtables

create,read,update,delete(seeCRUD)

CreateFreshApiTokenmiddleware,TokensfromLaravelsessionauthentication(synchronizertokens),TokensfromLaravelsessionauthentication(synchronizertokens)

create_users_tablemigration,DefiningMigrations,TheUserModelandMigration

cronjobs,schedulerasalternativeto,Scheduler

cross-siterequestforgery(CSRF),Glossary

CRUD(create,read,update,delete),Controllers

(seealsoresourcecontrollers)

CSRF(cross-siterequestforgery),CSRFProtection-CSRFProtection,BringingEchointoyourproject,Glossary

Page 712: Laravel: Up and Running: A Framework for Building Modern PHP

csrf_field()helper,Misc

csrf_token()helper,Misc

CSS

preprocessorfor,Elixir-Elixir,WhatDoesElixirProvide?,Glossary

preprocessorless,inElixir,PreprocessorlessCSS

customroutemodelbinding,CustomRouteModelBinding

D

databasefolder,TheFolders

databasenotifications,Databasenotifications

DatabaseMigrationstrait,DatabaseMigrations

databases,DatabaseandEloquent

(seealsoEloquent)

connectionsto,configuring,Configuration-OtherDatabaseConfigurationOptions

customguardprovidersfor,CreatingaCustomUserProvider

databasetypessupported,DatabaseConnections,QueryBuilder

Homestead,LaravelHomestead-ConnectingtoHomesteaddatabasesfromdesktopapplications,Glossary

migrations,Migrations-RunningMigrations

paginatingresultsfrom,PaginatingDatabaseResults-ManuallyCreatingPaginators

querybuilder,QueryBuilder-Transactions

seeders,Seeding-Definingandaccessingmultiplemodelfactorytypes

testing,Testing-Testing

Tinkerinteractingwith,Tinker

Page 713: Laravel: Up and Running: A Framework for Building Modern PHP

DatabaseSeederclass,Seeding

DatabaseTransactionstrait,DatabaseTransactions

database_path()helper,ApplicationPaths

datemutators,Datemutators

datesandtimes(seeCarbonpackage;scheduler;timestamps)

datetime()method,Blueprint,Creatingcolumns

dbcommands,Artisan,TheGroupedCommands

DBfacade,BasicUsageoftheDBFacade

db:seedcommand,Artisan,Seeding

dd()helper,Misc

DeadMan’sSnitch,HandlingTaskOutput

decimal()method,Blueprint,Creatingcolumns

decrement()method,Cache,TheMethodsAvailableonCacheInstances

decrement()method,DB,Updates

default()method,Blueprint,Buildingextrapropertiesfluently

define()method,Gate,TheGateFacade(andInjectingGate)

define()method,modelfactories,Creatingamodelfactory

delay()method,jobs,Customizingthedelay

delay()method,notification,QueueingNotifications

DELETEmethod,AnIntroductiontoHTTPVerbs-HTTPMethodSpoofinginHTMLForms

forresourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

Page 714: Laravel: Up and Running: A Framework for Building Modern PHP

routesbasedon,RouteVerbs

delete()method,DB,Rawdeletes,Deletes

delete()method,Eloquent,Normaldeletes

delete()method,Storage,UsingtheStorageFacade

delete()method,TestCase,“Visiting”Routes

deleteDirectory()method,Storage,UsingtheStorageFacade

deleted_atcolumn,Softdeletes

deletes,soft,Glossary

denies()method,Gate,TheGateFacade(andInjectingGate)

dependencyinjection,AQuickIntroductiontoDependencyInjection-DependencyInjectionandLaravel,Glossary

constructorinjection,AQuickIntroductiontoDependencyInjection,DependencyInjectionandLaravel,ConstructorInjection

methodinjection,AQuickIntroductiontoDependencyInjection,MethodInjection

setterinjection,AQuickIntroductiontoDependencyInjection

testingusing,Testing

destroy()method,resourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

detach()method,Eloquent,Manytomany

developmentenvironments,LocalDevelopmentEnvironments-ConnectingtoHomesteaddatabasesfromdesktopapplications

DI(dependencyinjection)container(seecontainer)

directives(Blade),BladeTemplating,Glossary

directories()method,Storage,UsingtheStorageFacade

Page 715: Laravel: Up and Running: A Framework for Building Modern PHP

disk()method,Storage,UsingtheStorageFacade,BasicFileUploadsandManipulation

dispatch()helper,Pushingajobontoaqueue,Misc

DispatchesJobstrait,Pushingajobontoaqueue

dissociate()method,Eloquent,Onetomany

distinct()method,DB,Constrainingmethods

dnsmasqtool,LaravelValet

domains,top-level,ConfiguringHomestead

dontSee()method,TestCase,CustomApplicationTestingAssertions

dontSeeInDatabase()method,TestCase,CustomApplicationTestingAssertions

dontSeeInField()method,TestCase,CustomApplicationTestingAssertions

dontSeeIsChecked()method,TestCase,CustomApplicationTestingAssertions

dontSeeIsSelected()method,TestCase,CustomApplicationTestingAssertions

dontSeeJson()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

dontSeeLink()method,TestCase,CustomApplicationTestingAssertions

dotnotation(.),Glossary

double()method,Blueprint,Creatingcolumns

downcommand,Artisan,BasicArtisanCommands

down()method,migrations,DefiningMigrations,DefiningMigrations

downloadresponses,Downloadresponses

download()method,Response,response()->download()and->file(),Downloadresponses

E

e()helper,TheStringHelpersandPluralization,Strings

Page 716: Laravel: Up and Running: A Framework for Building Modern PHP

@eachdirective,Blade,@each

each()method,collection,AFewMethods

eagerloading,Eagerloading-Eagerloadingonlythecount,Glossary

Echo,BroadcastingEventsoverWebSockets,andLaravelEcho,ReceivingtheMessage,AdvancedBroadcastingTools-SubscribingtonotificationswithEcho,Glossary

authorizationforchannels,BindingauthorizationdefinitionsforWebSocketchannels-BindingauthorizationdefinitionsforWebSocketchannels

excludinguserfromevents,Excludingthecurrentuserfrombroadcastevents,Excludingthecurrentuser

JavaScriptpackagefor,LaravelEcho(theJavaScriptSide)-SubscribingtonotificationswithEcho

listeningforevents,UsingEchoforbasiceventbroadcasting

presencechannels,Presencechannels

privatechannels,Privatechannelsandbasicauthentication

serviceproviderconfiguration,Thebroadcastserviceprovider

subscribingtochannels,UsingEchoforbasiceventbroadcasting

subscribingtonotifications,SubscribingtonotificationswithEcho

edit()method,resourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

Elixirbuildtool,Elixir-Elixirextensions,Glossary

directorystructurefor,ElixirFolderStructure

documentationfor,WhatDoesElixirProvide?

extensionsfor,Elixirextensions-Elixirextensions

JavaScript,concatenating,ConcatenatingJavaScript

Page 717: Laravel: Up and Running: A Framework for Building Modern PHP

JavaScript,processing,ProcessingJavaScript

multiplefiles,processing,Passingmultiplefiles

preprocessorlessCSS,PreprocessorlessCSS

productionmode,The--productionflag

running,RunningElixir

sourcemaps,generating,Sourcemaps

tests,running,Tests

versioning,Versioning-Versioning

versionsof,Elixir

elixir()helper,Versioning,URLs

Eloquent,Pagination,IntroductiontoEloquent-EloquentEvents,Glossary

accessors,Accessors,HidingattributesfromJSON,Testing,Glossary

aggregates,Aggregates

attributecasting,Attributecasting

collectionsreturnedby,EloquentCollections-WhatEloquentcollectionsadd

customzingroutekeyfor,ImplicitRouteModelBinding

datemutators,Datemutators

deletes,DeletingwithEloquent-Force-deletingsoft-deletedentities

eagerloading,Eagerloading-Eagerloadingonlythecount,Glossary

events,EloquentEvents-EloquentEvents

exceptionsthrownby,Getone

fillableorguardedproperties,Massassignment

Page 718: Laravel: Up and Running: A Framework for Building Modern PHP

filteringAPIresults,FilteringYourAPIResults

full-textsearchfor,Full-TextSearchwithLaravelScout-ManuallyTriggerIndexingviatheCLI

inserts,Inserts-Inserts,firstOrCreate()andfirstOrNew()

JSONresultsforAPIs,ControllerOrganizationandJSONReturns

massassignment,EloquentModelMassAssignment,Massassignment-Massassignment,Glossary

migration,creatingwithmodel,CreatingandDefiningEloquentModels

model,creating,CreatingandDefiningEloquentModels-Timestamps

mutators,Mutators-Mutators

paginationfor,PaginatingDatabaseResults-ManuallyCreatingPaginators,EloquentPagination-EloquentPagination

primarykeys,Primarykey

querybuilder,Pagination

relationships,EloquentRelationships-Manytomanypolymorphic

retrievingdata,RetrievingDatawithEloquent-Aggregates

scopes(filters),Scopes-Removingglobalscopes,Glossary

serialization,EloquentSerialization-HidingattributesfromJSON

sortingresults,SortingandFiltering-SortingYourAPIResults

tablenames,Tablename

timestamps,Creatingcolumns,Timestamps,Datemutators,ChildRecordsUpdatingParentRecordTimestamps-Eagerloadingonlythecount

transformingresults,TransformingResults-WritingYourOwnTransformer

Page 719: Laravel: Up and Running: A Framework for Building Modern PHP

updates,Updates-Massassignment

userinputfrom,EloquentModelMassAssignment

@elsedirective,Blade,@if,BladeChecks

@elseifdirective,Blade,@if

emailnotifications,Emailnotifications-Emailnotifications

emailOutputTo()method,tasks,HandlingTaskOutput

EncryptCookiesmiddleware,Cookies

encryption

disablingforcookies,Cookies

generatingkeysforapplication,TheGroupedCommands

generatingkeysforOAuthserver,InstallingPassport

manuallyencryptingcookies,Cookies

ofsessiondata,Sessions

@endcandirective,BladeChecks

@endcannotdirective,BladeChecks

@endifdirective,Blade,@if

@endsectiondirective,Blade,DefiningSectionswith@section/@showand@yield,@sectionand@endsection

ends_with()helper,TheStringHelpersandPluralization,Strings

@endunlessdirective,Blade,@unlessand@endunless

enum()method,Blueprint,Creatingcolumns

envcommand,Artisan,BasicArtisanCommands

Page 720: Laravel: Up and Running: A Framework for Building Modern PHP

.envfile,TheLooseFiles,Configuration

env()helper,Configuration,Misc

.env.examplefile,TheLooseFiles

.env.testfile,TheTestingEnvironment

environmentvariables,Glossary

returning,Misc

settingfortests,TheTestingEnvironment

environment()method,TheTestingEnvironment

Envoyer,HandlingTaskOutput,Glossary

equalsign(=),inArtisanargumentdefinition,Arguments,required,optional,and/orwithdefaults

errorbags,MessageBags-NamedErrorBags,TestingMessageandErrorBags

error()method,Output

errorsandexceptions

fromEloquent,Getone

fromHTTP,Misc

fromjobsinqueue,handling,HandlingErrors-Handlingfailedjobs

inmessageanderrorbags,MessageBags-NamedErrorBags,TestingMessageandErrorBags

fromsession,testingfor,JSONandNon-visit()ApplicationTestingAssertions,Session

fromuserinput,displaying,DisplayingValidationErrorMessages

$errorsvariable,MessageBags-NamedErrorBags

Page 721: Laravel: Up and Running: A Framework for Building Modern PHP

ES6,JavaScript,Elixir

eventcommands,Artisan,TheGroupedCommands

Eventfacade,FiringanEvent

event()helper,FiringanEvent,Misc

event-relatedtests,JobsandEvents

events,Events-Eventsubscribers,Glossary

authentication,AuthEvents

broadcastingoverWebSockets(seeWebSockets)

creating,FiringanEvent-FiringanEvent

Eloquent,EloquentEvents-EloquentEvents

firing,FiringanEvent-FiringanEvent,Misc

listenersfor,creating,ListeningforanEvent-ListeningforanEvent

Pub/Subpatternusedby,Events

subscribersfor,Eventsubscribers-Eventsubscribers

ExampleTest.phpfile,TestingBasics

except()method,Request,$request->except()and$request->only(),Basicuserinput

exceptions(seeerrorsandexceptions)

exists()method,Request,$request->has()and$request->exists(),Basicuserinput

exists()method,Storage,UsingtheStorageFacade

expectsEvents()method,TestCase,JobsandEvents

expectsJob()method,TestCase,JobsandEvents

$expressionparameter,Blade,ParametersinCustomBladeDirectives

Page 722: Laravel: Up and Running: A Framework for Building Modern PHP

extend()method,Storage,AddingAdditionalFlysystemProviders

@extendsdirective,Blade,@extends

F

facades,FacadesandtheContainer-HowFacadesWork,Glossary

accessingbackingclassof,HowFacadesWork

creating,HowFacadesWork

importing,GettingUserInput

importingnamespacesfor,FacadesandtheContainer

injectingbackingclassof,HowFacadesWork

mocking,MockingFacades

namespacesfor,JSONInput(and$request->json())

staticcallsusing,RouteDefinitions

factory()helper,Creatingamodelfactory,Usingamodelfactory,Misc

failed()method,jobs,Handlingfailedjobs

failing()method,Queue,Handlingfailedjobs

Faker,Testing,Returningfakefiles

Filefacade,UsingtheStorageFacade

fileresponses,Fileresponses

file()method,Faker,Returningfakefiles

file()method,Request,UploadedFiles,Files

file()method,Response,Testing,Fileresponses

Page 723: Laravel: Up and Running: A Framework for Building Modern PHP

files()method,Storage,UsingtheStorageFacade

filesystemstorage(seestorage)

filesystems.phpfile,ConfiguringFileAccess,UsingtheStorageFacade

file_get_contents()function,BasicFileUploadsandManipulation

fillableorguardedproperties,Massassignment

fillForm()method,TestCase,ClickingandForms

filter()method,collection,AFewMethods

filteringAPIresults,FilteringYourAPIResults

filters(seescopes(filters),Eloquent)

find()method,DB,Ending/returningmethods

find()method,Eloquent,Getone

findOrFail()method,DB,Ending/returningmethods

findOrFail()method,Eloquent,Getone

first()method,Blueprint,Buildingextrapropertiesfluently

first()method,collection,AFewMethods

first()method,DB,Ending/returningmethods,Ending/returningmethods

first()method,Eloquent,Getone

firstOrCreate()method,Eloquent,firstOrCreate()andfirstOrNew()

firstOrFail()method,DB,Ending/returningmethods

firstOrFail()method,Eloquent,Getone

firstOrNew()method,Eloquent,firstOrCreate()andfirstOrNew()

Page 724: Laravel: Up and Running: A Framework for Building Modern PHP

flags,Glossary

flash()method,Request,Persistence

flash()method,Session,FlashSessionStorage

flashExcept()method,Request,Persistence

flashOnly()method,Request,Persistence

float()method,Blueprint,Creatingcolumns

fluentinterface,QueryBuilder

fluentmethods,Glossary

flush()method,Cache,TheMethodsAvailableonCacheInstances

flush()method,Request,Persistence

flush()method,Session,TheMethodsAvailableonSessionInstances

flushSession()method,TestCase,AuthenticationandSessions

Flysystempackage,LocalandCloudFileManagers,AddingAdditionalFlysystemProviders,Glossary

followRedirects()method,TestCase,“Visiting”Routes

fontsusedinthisbook,HowThisBookIsStructured

forceDelete()method,Eloquent,Force-deletingsoft-deletedentities

forever()method,Cache,TheMethodsAvailableonCacheInstances

Forge,BasicQueueConfiguration,Glossary

forget()method,Cache,TheMethodsAvailableonCacheInstances

forget()method,Session,TheMethodsAvailableonSessionInstances

ForgotPasswordController,ForgotPasswordController

Page 725: Laravel: Up and Running: A Framework for Building Modern PHP

formencoding,UploadedFiles

formmethodspoofing,FormMethodSpoofing-HTTPMethodSpoofinginHTMLForms

formrequests,FormRequests-UsingaFormRequest,PassingParameterstoMiddleware

form-relatedtests,ClickingandForms-ClickingandForms

forUser()method,Gate,TheGateFacade(andInjectingGate),CheckingontheUserInstance

FQCN(fully-qualifiedclassname),Glossary

Fractalpackage,TransformingResults

frameworks,WhyLaravel?-Laravel5

(seealsoLaravel)

from()method,mailable,MethodsAvailableinbuild()

full-textsearch,Full-TextSearchwithLaravelScout-ManuallyTriggerIndexingviatheCLI

fully-qualifiedclassname(FQCN),Glossary

functionaltests(seeapplicationtests)

functions(seehelperfunctions)

G

Gatefacade,Authorization(ACL)andRoles,TheGateFacade(andInjectingGate)

GETmethod,AnIntroductiontoHTTPVerbs-HTTPMethodSpoofinginHTMLForms

forresourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

routesbasedon,RouteVerbs

get()method,Cache,AccessingtheCache,TheMethodsAvailableonCacheInstances

Page 726: Laravel: Up and Running: A Framework for Building Modern PHP

get()method,Cookie,TheCookiefacade

get()method,DB,ChainingwiththeQueryBuilder,Ending/returningmethods

get()method,Eloquent,RetrievingDatawithEloquent,Getmany

get()method,ParameterBag,Basicuserinput

get()method,Route,RouteNames

get()method,Session,TheMethodsAvailableonSessionInstances

get()method,Storage,UsingtheStorageFacade

get()method,TestCase,“Visiting”Routes

getFacadeAccessor()method,HowFacadesWork

getRealPath()method,SplFileInfo,BasicFileUploadsandManipulation

.gitignorefile,TheLooseFiles

give()method,ContextualBinding

globalscopes,Globalscopes-Removingglobalscopes

granttypes,Passport,Passport’sAvailableGrantTypes-TokensfromLaravelsessionauthentication(synchronizertokens)

groupBy()method,collection,AFewMethods

groupBy()method,DB,Modifyingmethods

guard()method,UsingOtherGuardsWithoutChangingtheDefault

guardedorfillableproperties,Massassignment

guards,Guards-CustomUserProvidersforNonrelationalDatabases

adding,AddingaNewGuard

default,changing,ChangingtheDefaultGuard

Page 727: Laravel: Up and Running: A Framework for Building Modern PHP

driverfor,Guards,AddingaNewGuard

providerfor,Guards-CustomUserProvidersforNonrelationalDatabases

selecting,UsingOtherGuardsWithoutChangingtheDefault

guestmiddleware,AuthMiddleware

guest()method,Usingtheauth()GlobalHelperandtheAuthFacade

guest()method,redirects,OtherRedirectMethods

Gulpbuildtool,Elixir,RunningElixir,Glossary

gulpcommand,RunningElixir

gulptddcommand,Tests

gulpwatchcommand,TestingwithElixir

gulpfile.jsfile,TheLooseFiles,Elixir

H

handle()method,events,ListeningforanEvent

handle()method,jobs,Creatingajob

handle()method,requests,Laravel’skernel,CreatingCustomMiddleware-BindingMiddleware,PassingParameterstoMiddleware

has()method,Cache,TheMethodsAvailableonCacheInstances

has()method,Cookie,TheCookiefacade

has()method,Eloquent,Selectingonlyrecordsthathavearelateditem

has()method,ParameterBag,Basicuserinput

has()method,Request,$request->has()and$request->exists(),Basicuserinput

has()method,Session,TheMethodsAvailableonSessionInstances

Page 728: Laravel: Up and Running: A Framework for Building Modern PHP

HasApiTokenstrait,InstallingPassport

hasCookie()method,Request,Persistence

hasFile()method,Request,UploadedFiles,Files

hasMany()method,Eloquent,Onetomany

hasManyThrough()method,Eloquent,Hasmanythrough

hasOne()method,Eloquent,Onetoone

having()method,DB,Modifyingmethods

havingRaw()method,DB,Modifyingmethods

HEADmethod,AnIntroductiontoHTTPVerbs-HTTPMethodSpoofinginHTMLForms

header()method,Request,Userandrequeststate,ReadingRequestHeadersinLaravel

header()method,Response,SendingResponseHeadersinLaravel

headers(seerequestheaders;responseheaders)

helpcommand,Artisan,BasicArtisanCommands

helperfunctions,Helpers-Misc,Glossary

(seealsospecifichelpers)

forarrays,Arrays-Arrays

forpaths,ApplicationPaths

forstrings,Strings-Strings

forURLs,URLs-URLs

here()method,Echo,Presencechannels

$hiddenproperty,TransformingResults

Page 729: Laravel: Up and Running: A Framework for Building Modern PHP

home()method,redirects,OtherRedirectMethods

Homestead,LaravelHomestead-ConnectingtoHomesteaddatabasesfromdesktopapplications,Glossary

configuring,ConfiguringHomestead-ConfiguringHomestead

databases,connectingto,ConnectingtoHomesteaddatabasesfromdesktopapplications

databases,creating,CreatingdatabasesinHomestead

dependenciesfor,InstallingHomestead’sdependencies

initializing,ProvisioningHomestead

installing,InstallingHomestead

settingup,SettingupHomestead-ConfiguringHomestead

startingandstopping,UsingHomesteadday-to-day

toolsprovidedwith,SettingupHomestead

Homestead.yamlfile,ConfiguringHomestead-ConfiguringHomestead

.htaccessfile,Laravel’sRequestLifecycle

htmlentities()function,EchoingData,Strings

HTTPmethods(verbs),RouteDefinitions,ThemethodsofLaravel’sresourcecontrollers,AnIntroductiontoHTTPVerbs-HTTPMethodSpoofinginHTMLForms

HTTPredirects,Redirects-redirect()->with(),Misc,Misc

HTTPrequests,AbortingtheRequest,Laravel’sRequestLifecycle-BootstrappingtheApplication,TheRequestObject-Persistence

(seealsoRequestobject)

HTTPresponses,CustomResponses,TheResponseObject-Customresponsemacros,Misc

Page 730: Laravel: Up and Running: A Framework for Building Modern PHP

(seealsoResponseobject)

HttpFoundationclasses,TheRequestObject

hyphen,double(--),precedingArtisancommandoptions,Options,requiredvalues,valuedefaults,andshortcuts

I

iconsusedinthisbook,ConventionsUsedinThisBook

id()method,Usingtheauth()GlobalHelperandtheAuthFacade

@ifdirective,Blade,@if

Illuminatenamespace,Glossary

implicitroutemodelbinding,ImplicitRouteModelBinding

@includedirective,Blade,@include

increment()method,Cache,TheMethodsAvailableonCacheInstances

increment()method,DB,Updates

increments()method,Blueprint,Creatingcolumns

index()method,Blueprint,Buildingextrapropertiesfluently

index()method,resourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

index.phpfile,BootstrappingtheApplication-ServiceProviders

info()method,Output

@injectdirective,Blade,BladeServiceInjection

Inputfacade,GettingUserInput

input()method,Request,$request->input(),JSONInput(and$request->json()),Basicuserinput

Page 731: Laravel: Up and Running: A Framework for Building Modern PHP

inRandomOrder()method,DB,Modifyingmethods

insert()method,DB,Rawinserts,Inserts

insertGetId()method,DB,Inserts

installertool,InstallingLaravelwiththeLaravelInstallerTool,UpandRunning

instances,bindingto,BindingtoSingletons,Aliases,andInstances

integer()method,Blueprint,Creatingcolumns

integrationtests,Testing,Glossary

intended()method,redirects,OtherRedirectMethods

InteractsWithQueuetrait,Creatingajob

interfaces(contracts),TheUserModelandMigration,BindingaConcreteInstancetoanInterface,Glossary

Interventionlibrary,BasicFileUploadsandManipulation

IoC(inversionofcontrol),AQuickIntroductiontoDependencyInjection,BindingaConcreteInstancetoanInterface,Testing,Glossary

IoCcontainer(seecontainer)

ip()method,Request,Userandrequeststate

is()method,Request,Userandrequeststate

isEmpty()method,collection,AFewMethods

isJson()method,Request,Userandrequeststate

isValid()method,File,UploadedFiles

J

JavaScript

concatenating,inElixir,ConcatenatingJavaScript

Page 732: Laravel: Up and Running: A Framework for Building Modern PHP

escapingbackslashesin,ReceivingtheMessage

processing,inElixir,ProcessingJavaScript

JavaScriptES6,Elixir

JavaScriptObjectNotation(seeJSON)

Jobclass,Glossary

job-relatedtests,JobsandEvents

jobs,Queues,QueuedJobs-Customizingthedelay

(seealsoqueues)

creating,Creatingajob-Creatingajob

deleting,Handlingfailedjobs

dispatching,Misc

failed,handling,HandlingErrors-Handlingfailedjobs

numberoftriesfor,Limitingthenumberoftries

pushingontoqueue,Pushingajobontoaqueue-Customizingthedelay

releasingbacktoqueue,ControllingtheQueue

retrying,Handlingfailedjobs

join()method,DB,Joins

join()method,Echo,Presencechannels

JSON(JavaScriptObjectNotation),Glossary

APIpatternfor,TheBasicsofREST-LikeJSONAPIs

APIspecfor,SortingandFiltering

assertions,JSONandNon-visit()ApplicationTestingAssertions-JSONandNon-visit()

Page 733: Laravel: Up and Running: A Framework for Building Modern PHP

ApplicationTestingAssertions

operations,JSONoperations,EloquentSerialization-HidingattributesfromJSON

responses,JSONresponses

testing,TestingBasics

JSONWebToken(JWT),TokensfromLaravelsessionauthentication(synchronizertokens),Glossary

json()method,Blueprint,Creatingcolumns

json()method,Request,JSONInput(and$request->json()),Basicuserinput

json()method,Response,response()->json()and->jsonp(),JSONresponses

json()method,TestCase,“Visiting”Routes

jsonb()method,Blueprint,Creatingcolumns

jsonp()method,Response,response()->json()and->jsonp()

JWT(JSONWebToken),TokensfromLaravelsessionauthentication(synchronizertokens),Glossary

K

keep()method,Session,FlashSessionStorage

kernel,Laravel’skernel

Kernel.phpfile,Bindingglobalmiddleware

keycommands,Artisan,TheGroupedCommands

keys()method,ParameterBag,Basicuserinput

L

Lambopackage,UpandRunning

Laravel

Page 734: Laravel: Up and Running: A Framework for Building Modern PHP

advantagesof,What’sSoSpecialAboutLaravel?-HowLaravelAchievesDeveloperHappiness

communityfor,TheLaravelCommunity-TheLaravelCommunity

documentationfor,WhatThisBookIsAbout

installer,CreatingaNewLaravelProject,UpandRunning

localdevelopmentenvironmentsfor,LocalDevelopmentEnvironments-ConnectingtoHomesteaddatabasesfromdesktopapplications

PHPversionsandextensionsfor,SystemRequirements

starting,UpandRunning

systemrequirements,WhoThisBookIsFor,SystemRequirements-Composer

versionsof(seeversionsofLaravel)

LaravelEcho(seeEcho)

LaravelEnvoyer,HandlingTaskOutput

LaravelForge(seeForge)

laravelnewcommand(Laravelinstaller),InstallingLaravelwiththeLaravelInstallerTool,UpandRunning

laravel.logfile,Thelogdriver

last()method,collection,AFewMethods

lastModified()method,Storage,UsingtheStorageFacade

later()method,Mail,later()

latest()method,DB,Modifyingmethods

lazyloading,Eagerloading,Lazyeagerloading

leftJoin()method,DB,Joins

Page 735: Laravel: Up and Running: A Framework for Building Modern PHP

LengthAwarePaginatorclass,ManuallyCreatingPaginators

lifecycleofapplication,Laravel’sRequestLifecycle-ServiceProviders

line()method,Output

links()method,PaginatingDatabaseResults

listcommand,Artisan,AnIntroductiontoArtisan

listen()method,Echo,UsingEchoforbasiceventbroadcasting

listeners,forevents,ListeningforanEvent-ListeningforanEvent

localdevelopmentenvironments,LocalDevelopmentEnvironments-ConnectingtoHomesteaddatabasesfromdesktopapplications

localdisk,ConfiguringFileAccess

localscopes,Localscopes

localization,Localization-Pluralizationinlocalization,TranslationandLocalization

Logfacade,FacadesandtheContainer-HowFacadesWork

logging,FacadesandtheContainer-HowFacadesWork,Thelogdriver

login()method,AuthenticatesUserstrait,ManuallyAuthenticatingUsers

LoginController,LoginController-ThrottlesLoginstrait

loginUsingId()method,ManuallyAuthenticatingUsers

longText()method,Blueprint,Creatingcolumns

$loopvariable,@forelse

loops(Blade),Loops-@forelse,@each

M

mail,Mail-Universalto

Page 736: Laravel: Up and Running: A Framework for Building Modern PHP

attachments,MethodsAvailableinbuild()-Queues

capturing,Mailtrap.io

classicmail,“Classic”Mail

configuring,Mail

creating,Basic“Mailable”MailUsage-Basic“Mailable”MailUsage

customizing,MethodsAvailableinbuild()

driverssupported,Mail

HTMLformat,MailTemplates

inlineimages,AttachmentsandInlineImages

logging,Thelogdriver

mailablemail,Basic“Mailable”MailUsage-Basic“Mailable”MailUsage

manuallymodifying,MethodsAvailableinbuild()

plaintextformat,MailTemplates

queuesfor,Queues

sending,Basic“Mailable”MailUsage

templates,MailTemplates

testing,LocalDevelopment,Mail

universalto,Universalto

Mailfacade,“Classic”Mail

mail.phpfile,Mail,Universalto

MailThief,Mail

Page 737: Laravel: Up and Running: A Framework for Building Modern PHP

Mailtrap,Mailtrap.io

makecommands,Artisan,TheGroupedCommands

make()method,app,Theapp()GlobalHelper

make()method,Cookie,TheCookiefacade,Thecookie()globalhelper

make()method,modelfactories,Usingamodelfactory

make()method,Response,response()->make()

make:authcommand,Artisan,TheAuthScaffold,TheVuecomponents

make:controllercommand,Artisan,Controllers,Controllers,Controllers,ResourceControllers

make:eventcommand,Artisan,FiringanEvent

make:jobcommand,Artisan,Creatingajob

make:mailcommand,Artisan,Basic“Mailable”MailUsage

make:middlewarecommand,Artisan,CreatingCustomMiddleware

make:migrationcommand,Artisan,Creatingamigration

make:modelcommand,Artisan,CreatingandDefiningEloquentModels,ControllerOrganizationandJSONReturns

make:notificationcommand,Artisan,Notifications

make:policycommand,Artisan,Generatingpolicies

make:requestcommand,Artisan,CreatingaFormRequest

make:seederArtisancommand,CreatingaSeeder

makeVisible()method,Eloquent,HidingattributesfromJSON

many-to-manyrelationships,EloquentRelationships,Manytomany-Gettingdatafromthepivottable

Page 738: Laravel: Up and Running: A Framework for Building Modern PHP

map()method,collection,AFewMethods

mapApiRoutes()method,RouteServiceProvider,Usingmiddlewaregroups

mapWebRoutes()method,RouteServiceProvider,Usingmiddlewaregroups

massassignment,EloquentModelMassAssignment,Massassignment-Massassignment,Glossary

max()method,DB,Ending/returningmethods

MbstringPHPextension,SystemRequirements

mediumInteger()method,Blueprint,Creatingcolumns

mediumText()method,Blueprint,Creatingcolumns

Memcacheddatastore,OtherDatabaseConfigurationOptions,Glossary

messagebags,MessageBags-NamedErrorBags,TestingMessageandErrorBags

MessageBagclass,MessageBags-NamedErrorBags

methodinjection,AQuickIntroductiontoDependencyInjection,MethodInjection

method()method,Request,Userandrequeststate

methods,RouteNames

(seealsospecificmethods)

chaining,RouteNames,Middleware

fluent,Glossary

HTTPmethods(verbs),RouteDefinitions,ThemethodsofLaravel’sresourcecontrollers,AnIntroductiontoHTTPVerbs-HTTPMethodSpoofinginHTMLForms

middleware,RouteDefinitions,LaravelandMiddleware-PassingParameterstoMiddleware,Glossary

Page 739: Laravel: Up and Running: A Framework for Building Modern PHP

binding,BindingMiddleware-Usingmiddlewaregroups

custom,creating,CreatingCustomMiddleware-BindingMiddleware

forauthentication,AuthMiddleware

forauthorization,TheAuthorizeMiddleware

groups,Usingmiddlewaregroups

passingparametersto,PassingParameterstoMiddleware

routegroupsfor,Middleware-Middleware

middleware()method,Middleware,Usingmiddlewaregroups

migratecommands,Artisan,BasicArtisanCommands,TheGroupedCommands,RunningMigrations,Seeding,ControllerOrganizationandJSONReturns

migrations,Migrations-RunningMigrations,Glossary

columns,creating,Creatingcolumns-Creatingcolumns

columns,modifying,Modifyingcolumns-Indexesandforeignkeys

creatingwithEloquentmodel,CreatingandDefiningEloquentModels

defining,DefiningMigrations-Addingandremovingforeignkeys

fieldproperties,setting,Buildingextrapropertiesfluently

foreignkeys,adding,Addingandremovingforeignkeys

foreignkeys,dropping,Addingandremovingforeignkeys

indexes,adding,Indexesandforeignkeys

indexes,removing,Removingindexes

running,RunningMigrations

tables,creating,Creatingtables

Page 740: Laravel: Up and Running: A Framework for Building Modern PHP

tables,dropping,Droppingtables

typesof,DefiningMigrations

min()method,DB,Ending/returningmethods

mix.browserify()method,ProcessingJavaScript

mix.phpSpec()method,Tests,TestingwithElixir

mix.phpUnit()method,Tests,TestingwithElixir

mix.rollup()method,ProcessingJavaScript

mix.sass()method,Elixir,Passingmultiplefiles

mix.scripts()method,ConcatenatingJavaScript

mix.styles()method,PreprocessorlessCSS

mix.version()method,Versioning

mix.webpack()method,ProcessingJavaScript

Mockerylibrary,Testing,Mocking-MockingFacades,Glossary

modelfactory,ModelFactories-Definingandaccessingmultiplemodelfactorytypes,Glossary

Model-View-Controller(MVC)pattern(seecontrollers)(seeviews)

modelKeys()method,collection,WhatEloquentcollectionsadd

morphedByMany()method,Eloquent,Manytomanypolymorphic

morphs()method,Blueprint,Creatingcolumns

morphsTo()method,Eloquent,Polymorphic

morphToMany()method,Eloquent,Manytomanypolymorphic

move()method,Storage,UsingtheStorageFacade

Page 741: Laravel: Up and Running: A Framework for Building Modern PHP

multitenancy,Example:UsingCustomBladeDirectivesforaMultitenantApp-Example:UsingCustomBladeDirectivesforaMultitenantApp,Glossary

mutators,Mutators-Mutators,Glossary

MVC(Model-View-Controller)pattern(seecontrollers;views)

N

nameprefixes,routegroupsfor,NamePrefixes

name()method,RouteNames

namespaceprefixes,routegroupsfor,NamespacePrefixes

namespaces

forcontracts,TheUserModelandMigration

forcontrollers,Controllers

defaultAppnamespace,replacing,TheGroupedCommands

escapingbackslashesinJavaScript,ReceivingtheMessage

forfacades,GettingUserInput,JSONInput(and$request->json()),FacadesandtheContainer

inFQCN(fully-qualifiedclassname),Glossary

Illuminate,BootstrappingtheApplication,Glossary

makenamespace,forArtisan,Controllers,WritingCustomArtisanCommands

needs()method,ContextualBinding

Nexmo,SMSnotifications

Nginxwebserver,Glossary

Node.js,installing,RunningElixir

Notifiabletrait,SendingNotifications

Page 742: Laravel: Up and Running: A Framework for Building Modern PHP

Notificationfacade,SendingnotificationswiththeNotificationfacade

notifications,Notifications-Slacknotifications

broadcastnotifications,Broadcastnotifications

channelsfor,Notifications,Definingthevia()MethodforYourNotifiables

creating,Notifications-Definingthevia()MethodforYourNotifiables

databasenotifications,Databasenotifications

driverssupported,Out-of-the-BoxNotificationTypes

emailnotifications,Emailnotifications-Emailnotifications

queueing,QueueingNotifications

recipientsof,Notifications

sending,SendingNotifications

Slacknotifications,Slacknotifications

SMSnotifications,SMSnotifications

subscribingto,SubscribingtonotificationswithEcho

testing,Notifications

notificationscommands,Artisan,TheGroupedCommands

notify()method,Notifiable,SendingnotificationsusingtheNotifiabletrait

npminstallcommand,BringingEchointoyourproject

nullable()method,Blueprint,Buildingextrapropertiesfluently

nullableTimestamps()method,Blueprint,Creatingcolumns

O

OAuth2.0,Overridingpolicies,ABriefIntroductiontoOAuth2.0

Page 743: Laravel: Up and Running: A Framework for Building Modern PHP

(seealsoPassportpackage)

object-relationalmapper(seeORM)

Observerpattern,eventsfor,Glossary

old()helper,redirect()->with(),Misc

old()method,Request,Persistence

oldest()method,DB,Modifyingmethods

onConnection()method,jobs,Customizingtheconnection

onConnection()method,mailable,Specifyingthequeueorconnection

one-to-manyrelationships,EloquentRelationships,Onetomany-Onetomany

one-to-onerelationships,Onetoone-Onetoone

onlineresources

Elixirdocumentation,WhatDoesElixirProvide?

facadesdocumentation,HowFacadesWork

forthisbook,HowtoContactUs

Homesteaddocumentation,InstallingHomestead

Laraveldocumentation,WhatThisBookIsAbout

SSHkeys,creating,ConfiguringHomestead

Valetdocumentation,LaravelValet

only()method,Request,$request->except()and$request->only(),EloquentModelMassAssignment,Massassignment,Basicuserinput

onlyTrashed()method,Eloquent,Queryingwithsoftdeletes

onQueue()method,events,BroadcastinganEvent

Page 744: Laravel: Up and Running: A Framework for Building Modern PHP

onQueue()method,jobs,Customizingthequeue

onQueue()method,mailable,Specifyingthequeueorconnection

onUserSubscription()method,events,Eventsubscribers

OpenSSLPHPextension,SystemRequirements

operatingsystemrequirements,WhoThisBookIsFor,SystemRequirements

optimizecommand,Artisan,BasicArtisanCommands,Options

option()method,option()

options(Artisan),Glossary

OPTIONSmethod,AnIntroductiontoHTTPVerbs-HTTPMethodSpoofinginHTMLForms

orhelper,Blade,or

orderBy()method,DB,Modifyingmethods

orderBy()method,Eloquent,RetrievingDatawithEloquent,SortingYourAPIResults

ORM(object-relationalmapper),Pagination,Glossary

(seealsoEloquent)

orWhere()method,DB,Constrainingmethods-Constrainingmethods

P

package.jsonfile,TheLooseFiles

paginate()method,PaginatingDatabaseResults,EloquentPagination-EloquentPagination

pagination,Pagination-ManuallyCreatingPaginators

Paginatorclass,ManuallyCreatingPaginators

parameterbinding,PDO,Parameterbindingsandnamedbindings

Page 745: Laravel: Up and Running: A Framework for Building Modern PHP

ParameterBagclass,Basicuserinput

@parentdirective,Blade,@parent

Passportpackage,Overridingpolicies,APIAuthenticationwithLaravelPassport-PassportScopes,Glossary

granttypesfor,Passport’sAvailableGrantTypes-TokensfromLaravelsessionauthentication(synchronizertokens)

installing,InstallingPassport-InstallingPassport

routesfor,InstallingPassport,Passport’sAPI,Theroutes

scopes,PassportScopes-PassportScopes

Vuecomponents,TheVuecomponents-TheVuecomponents

PassportServiceProvider,InstallingPassport

passwordgrant,Passport,Passwordgrant-Passwordgrant

PATCHmethod,AnIntroductiontoHTTPVerbs-HTTPMethodSpoofinginHTMLForms

forresourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

routesbasedon,RouteVerbs

patch()method,TestCase,“Visiting”Routes

pathprefixes,routegroupsfor,PathPrefixes

path()method,Request,Userandrequeststate

paths

forfacades,JSONInput(and$request->json())

helpersfor,ApplicationPaths

inFQCN(fully-qualifiedclassname),Glossary

Page 746: Laravel: Up and Running: A Framework for Building Modern PHP

PDOparameterbinding,Parameterbindingsandnamedbindings

PDOPHPextension,SystemRequirements

period(.),dotnotation,Glossary

personalaccessclient,Personalaccesstokens

personalaccesstokens,Passport,Personalaccesstokens

PHP

versionsandextensionsfor,SystemRequirements

viewsrenderedwith,Views

PHPSpectestingframework,Glossary

PHPUnittestingframework,Testing,Testing,Glossary

phpunit.xmlfile,TheLooseFiles,TheTestingEnvironment

pingBefore()method,tasks,HandlingTaskOutput

pivottable,Manytomany-Gettingdatafromthepivottable

pjax()method,Request,Userandrequeststate

pluck()method,collection,AFewMethods

pluralization,TheStringHelpersandPluralization,Pluralizationinlocalization

policiesforauthorization,Policies-Overridingpolicies

polymorphicrelationships,EloquentRelationships,Polymorphic-Manytomanypolymorphic,Glossary

POSTmethod,AnIntroductiontoHTTPVerbs-HTTPMethodSpoofinginHTMLForms

forresourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

gettinguserinputfrom,GettingUserInput

Page 747: Laravel: Up and Running: A Framework for Building Modern PHP

routesbasedon,RouteVerbs

post()method,TestCase,“Visiting”Routes

prepend()method,Storage,UsingtheStorageFacade

preprocessors,Glossary

press()method,TestCase,ClickingandForms

primarykeys,Glossary

primary()method,Blueprint,Buildingextrapropertiesfluently

priority()method,mailable,MethodsAvailableinbuild()

private()method,Echo,Privatechannelsandbasicauthentication

progressbars,Progressbars

progressAdvance()method,Progressbars

progressFinish()method,Progressbars

progressStart()method,Progressbars

projects

configuring,Configuration

creating,CreatingaNewLaravelProject-InstallingLaravelwithComposer’screate-projectFeature

directorystructurefor,Laravel’sDirectoryStructure-TheLooseFiles

provides()method,serviceproviders,ServiceProviders

Pub/Subpattern,Events,BroadcastingEventsoverWebSockets,andLaravelEcho,Glossary

publicdisk,ConfiguringFileAccess

Page 748: Laravel: Up and Running: A Framework for Building Modern PHP

publicfolder,TheFolders

pull()method,Cache,TheMethodsAvailableonCacheInstances

pull()method,Session,TheMethodsAvailableonSessionInstances

push()method,Session,TheMethodsAvailableonSessionInstances

Pusher,ConfigurationandSetup,ReceivingtheMessage-BindingauthorizationdefinitionsforWebSocketchannels

PUTmethod,AnIntroductiontoHTTPVerbs-HTTPMethodSpoofinginHTMLForms

forresourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

routesbasedon,RouteVerbs

put()method,Cache,TheMethodsAvailableonCacheInstances

put()method,Session,TheMethodsAvailableonSessionInstances

put()method,Storage,UsingtheStorageFacade,BasicFileUploadsandManipulation

put()method,TestCase,“Visiting”Routes

putFile()method,Storage,UsingtheStorageFacade

Q

querybuilder,QueryBuilder-Transactions

(seealsoEloquent)

aggregates,Ending/returningmethods

chainingmethodswith,ChainingwiththeQueryBuilder-JSONoperations

constrainingqueries,Constrainingmethods-Constrainingmethods

databasetypessupported,QueryBuilder

DBfacadefor,BasicUsageoftheDBFacade

deletes,Deletes

Page 749: Laravel: Up and Running: A Framework for Building Modern PHP

inserts,Inserts

joins,Joins

JSONoperations,JSONoperations

modifyingqueries,Modifyingmethods-Modifyingmethods

multiplequeryresults,formatfor,Rawselects

paginationfor,PaginatingDatabaseResults-ManuallyCreatingPaginators,EloquentPagination

parameterbinding,Parameterbindingsandnamedbindings

rawSQLqueries,RawSQL-Rawdeletes,WritingrawqueriesinsidequerybuildermethodswithDB::raw

relationshipsas,Usingrelationshipsasquerybuilders

returningresults,Ending/returningmethods-Ending/returningmethods

transactions,Transactions

unions,Unions

updates,Updates

questionmark(?)

followingoptionalArtisancommandarguments,Arguments,required,optional,and/orwithdefaults

followingoptionalparameters,RouteParameters

queryparameters,Parameterbindingsandnamedbindings

question()method,Output

queuecommands,Artisan,TheGroupedCommands

queue()method,Cookie,TheCookiefacade

Page 750: Laravel: Up and Running: A Framework for Building Modern PHP

queue()method,Mail,queue()

queue.phpfile,BasicQueueConfiguration

queue:failedcommand,Artisan,Handlingfailedjobs

queue:failed-tablecommand,Artisan,Handlingfailedjobs

queue:flushcommand,Artisan,Handlingfailedjobs

queue:forgetcommand,Artisan,Handlingfailedjobs

queue:listencommand,Artisan,Limitingthenumberoftries

queue:retryallcommand,Artisan,Handlingfailedjobs

queue:retrycommand,Artisan,Handlingfailedjobs

queue:workcommand,Artisan,RunningaQueueWorker,Limitingthenumberoftries

Queueabletrait,Creatingajob

queues,Queues-QueuesSupportingOtherFunctions,Glossary

forArtisancommands,TheGroupedCommands,QueuesSupportingOtherFunctions

beanstalkdfor,Glossary

benefitsof,WhyQueues?

configuring,BasicQueueConfiguration

creatingjobsin,Creatingajob-Creatingajob

deletingjobsin,Handlingfailedjobs

dispatchingjobsin,Misc

errorswith,handling,HandlingErrors-Handlingfailedjobs

forjobs,QueuedJobs

formail,Queues,QueuesSupportingOtherFunctions

Page 751: Laravel: Up and Running: A Framework for Building Modern PHP

numberoftriesforjobs,Limitingthenumberoftries

providersanddriversfor,Queues

pushingjobsonto,Pushingajobontoaqueue-Customizingthedelay

releasingjobsbackto,ControllingtheQueue

retryingjobs,Handlingfailedjobs

workers,RunningaQueueWorker,ConfigurationandSetup

R

raw()method,DB,WritingrawqueriesinsidequerybuildermethodswithDB::raw

read-evaluate-print-loop(REPL)(seeTinker)

readme.mdfile,TheLooseFiles

Redirectfacade,Redirects

redirect()helper,Redirects-redirect()->with(),Redirectresponses,Misc

redirectPath()method,RegistersUserstrait

redirects,Redirects-redirect()->with()

Redis,OtherDatabaseConfigurationOptions,ReceivingtheMessage,ReceivingtheMessage,Glossary

reduce()method,collection,AFewMethods

reflash()method,Session,FlashSessionStorage

refresh()method,redirects,OtherRedirectMethods

regenerate()method,Session,TheMethodsAvailableonSessionInstances

register()method,RegisterUsers,RegistersUserstrait

register()method,serviceproviders,ServiceProviders,BindingtoaClosure,Service

Page 752: Laravel: Up and Running: A Framework for Building Modern PHP

Providers

RegisterController,RegisterController-RegistersUserstrait

RegistersUserstrait,RegistersUserstrait

regularexpressions

passingtostr_is(),Strings

routeconstraintsusing,RouteParameters

reject()method,collection,AFewMethods

relationships,EloquentRelationships-Manytomanypolymorphic

asquerybuilders,Usingrelationshipsasquerybuilders

eagerloading,Eagerloading-Eagerloadingonlythecount,Glossary

insertingrelateditems,Onetoone

lazyloading,Eagerloading,Lazyeagerloading

serializationof,HidingattributesfromJSON

release()method,jobs,ControllingtheQueue

remembermeaccesstoken,“RememberMe”

remember()method,Cache,TheMethodsAvailableonCacheInstances

rememberForever()method,Cache,TheMethodsAvailableonCacheInstances

rememberToken()method,Blueprint,Creatingcolumns

render()method,pagination,PaginatingDatabaseResults

REPL(read-evaluate-print-loop)(seeTinker)

RepresentationalStateTransfer(REST),TheBasicsofREST-LikeJSONAPIs-TheBasicsofREST-LikeJSONAPIs,Glossary

Page 753: Laravel: Up and Running: A Framework for Building Modern PHP

Requestfacade,InjectingaRequestObject

requestheaders,ReadingandSendingHeaders,ReadingRequestHeadersinLaravel

Requestobject,InjectingaRequestObject-JSONInput(and$request->json()),TheRequestObject-Persistence

accessing,GettingaRequestObjectinLaravel-Files

arrayinput,accessing,ArrayInput

capturingdirectly,TheRequestObject

filehandlingmethods,Files

formrequests,PassingParameterstoMiddleware

headersfor,ReadingandSendingHeaders,ReadingRequestHeadersinLaravel

JSONinput,accessing,JSONInput(and$request->json())

lifecycleof,Laravel’sRequestLifecycle-ServiceProviders

persistenceof,forsessioninteraction,Persistence

readingcookiesfrom,Readingcookiesfromrequestobjects

testing,Testing-Testing

typehintinginconstructors,InjectingDependenciesintoControllers,GettingaRequestObjectinLaravel-GettingaRequestObjectinLaravel

userandrequeststatemethods,Userandrequeststate

userinputmethods,Basicuserinput-Userandrequeststate

request()helper,InjectingaRequestObject,JSONInput(and$request->json()),GettingaRequestObjectinLaravel

reset()method,ResetPasswordController

resetPassword()method,ResetPasswordController

Page 754: Laravel: Up and Running: A Framework for Building Modern PHP

ResetPasswordController,ResetPasswordController

resourcecontrollerbinding,Bindingaresourcecontroller

resourcecontrollers,ResourceControllers-Bindingaresourcecontroller,ControllerOrganizationandJSONReturns-ControllerOrganizationandJSONReturns

resourcesfolder,TheFolders,ElixirFolderStructure

resources,API,TheBasicsofREST-LikeJSONAPIs,NestingandRelationships-NestingandRelationships

resources,online(seeonlineresources)

responseheaders,ReadingandSendingHeaders

Responseobject,TheResponseObject-Customresponsemacros

creating,UsingandCreatingResponseObjectsinControllers-Addingcookies

custom,CustomResponses

customresponsemacros,Customresponsemacros

downloadresponses,Downloadresponses

fileresponses,Fileresponses

headersfor,ReadingandSendingHeaders

JSONresponses,JSONresponses

lifecycleof,Laravel’sRequestLifecycle-ServiceProviders

redirectresponses,Redirectresponses-Customresponsemacros

settingcookieson,Settingcookiesonresponseobjects

testing,Testing-Testing

viewresponses,Viewresponses

Page 755: Laravel: Up and Running: A Framework for Building Modern PHP

response()helper,CustomResponses,UsingandCreatingResponseObjectsinControllers,Misc

REST(RepresentationalStateTransfer),TheBasicsofREST-LikeJSONAPIs-TheBasicsofREST-LikeJSONAPIs,Glossary

restore()method,Eloquent,Restoringsoft-deletedentities

reverse()method,collection,AFewMethods

rightanglebracket,triple(>>>),Tinkerprompt,Tinker

rollBack()method,DB,Transactions

Rollup,ProcessingJavaScript

routecaching,RouteCaching

routecommands,Artisan,TheGroupedCommands

routegroups,RouteGroups-NamePrefixes

(seealsocontrollers)

defining,RouteGroups

middlewareappliedto,Middleware-Middleware

nameprefixesusing,NamePrefixes

namespaceprefixesusing,NamespacePrefixes

pathprefixesusing,PathPrefixes

subdomainroutingusing,SubdomainRouting

routemiddleware,AuthMiddleware,TheAuthorizeMiddleware,Bindingroutemiddleware

routemodelbinding,RouteModelBinding-CustomRouteModelBinding

route()helper,RouteNames,RouteNames-RouteNames,URLs

Page 756: Laravel: Up and Running: A Framework for Building Modern PHP

route()method,redirect()->route()

route:cachecommand,Artisan,RouteCaching

route:listcommand,Artisan,Bindingaresourcecontroller

routes,Glossary

defining,RouteDefinitions-RouteNames

fluentdefinitionsof,RouteNames

handling,RouteHandling

listing,Bindingaresourcecontroller

naming,RouteNames-RouteNames

parametersfor,RouteParameters-RouteParameters,RouteModelBinding-CustomRouteModelBinding,FromRouteParameters

testing,Testing

verbsfor,RouteVerbs

routesfolder,TheFolders

routes()method,Auth,Auth::routes()

routes.phpfile,RouteDefinitions,Usingmiddlewaregroups

RouteServiceProvider,ServiceProviders,Usingmiddlewaregroups

rules(abilities)forauthorization,DefiningAuthorizationRules

rules()method,formrequest,CreatingaFormRequest

S

S3cloudstorage,ConfiguringFileAccess

s3disk,ConfiguringFileAccess

Page 757: Laravel: Up and Running: A Framework for Building Modern PHP

SaaS(SoftwareasaService),Glossary

save()method,Eloquent,Inserts

schedulecommands,Artisan,TheGroupedCommands

schedule:runcommand,Artisan,AvailableTaskTypes

scheduler,Scheduler-TaskHooks

Artisancommandsastasks,AvailableTaskTypes

avoidingtasksoverlapping,BlockingandOverlap

closuresastasks,AvailableTaskTypes,HandlingTaskOutput

shellcommandsastasks,AvailableTaskTypes

taskoutput,handling,HandlingTaskOutput-HandlingTaskOutput

tasktypes,AvailableTaskTypes

timeframesfor,setting,AvailableTimeFrames-AvailableTimeFrames

scopes(filters),Eloquent,Scopes-Removingglobalscopes,Glossary

scopes(privileges),OAuth,PassportScopes-PassportScopes

Scoutpackage,Full-TextSearchwithLaravelScout-ManuallyTriggerIndexingviatheCLI,Glossary

driverssupported,Full-TextSearchwithLaravelScout

installingandconfiguring,InstallingScout

manuallytriggering,ManuallyTriggerIndexingviaCode

markingmodelforindexing,MarkingYourModelforIndexing

notusingforsomeoperations,PerformOperationsWithoutIndexing

queuingactionsof,QueuesandScout

Page 758: Laravel: Up and Running: A Framework for Building Modern PHP

searchingindex,SearchingYourIndex

scout.phpfile,InstallingScout,QueuesandScout

scout:importcommand,Artisan,ManuallyTriggerIndexingviatheCLI

ScoutServiceProvider,InstallingScout

scriptinjection,{{Versus{!!

search()method,SearchingYourIndex

Searchabletrait,MarkingYourModelforIndexing

searchable()method,ManuallyTriggerIndexingviaCode

searchableAs()method,MarkingYourModelforIndexing

secret()method,Prompts

@sectiondirective,Blade,DefiningSectionswith@section/@showand@yield-@extends

sections,Blade,DefiningSectionswith@section/@showand@yield

secure()method,redirects,OtherRedirectMethods

secure()method,Request,Userandrequeststate

security

authentication(seeauthentication)

authorization(seeauthorization)

CSRF(cross-siterequestforgery),CSRFProtection-CSRFProtection,BringingEchointoyourproject,Glossary

encryption(seeencryption)

massassignment,EloquentModelMassAssignment,Massassignment-Massassignment,Glossary

Page 759: Laravel: Up and Running: A Framework for Building Modern PHP

scriptinjection,{{Versus{!!

see()method,TestCase,CustomApplicationTestingAssertions

seeCookie()method,TestCase,CustomApplicationTestingAssertions,Cookies

seed()method,TestCase,ArtisanandSeed

seeders,Seeding-Definingandaccessingmultiplemodelfactorytypes

creating,CreatingaSeeder

modelfactoriesfor,ModelFactories-Definingandaccessingmultiplemodelfactorytypes

testing,ArtisanandSeed

seeHeader()method,TestCase,CustomApplicationTestingAssertions

seeInDatabase()method,TestCase,CustomApplicationTestingAssertions

seeInField()method,TestCase,CustomApplicationTestingAssertions

seeIsChecked()method,TestCase,CustomApplicationTestingAssertions

seeIsSelected()method,TestCase,CustomApplicationTestingAssertions

seeJson()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

seeJsonEquals()method,TestCase,JSONandNon-visit()ApplicationTestingAssertions

seeLink()method,TestCase,CustomApplicationTestingAssertions

seePageIs()method,TestCase,CustomApplicationTestingAssertions

seePlainCookie()method,TestCase,Cookies

segment()method,Request,FromRequest

segments()method,Request,FromRequest

select()method,DB,Rawselects,Constrainingmethods

Page 760: Laravel: Up and Running: A Framework for Building Modern PHP

select()method,TestCase,ClickingandForms

selectRaw()method,DB,WritingrawqueriesinsidequerybuildermethodswithDB::raw

send()method,Mail,“Classic”Mail

send()method,Notification,SendingnotificationswiththeNotificationfacade

sendOutputTo()method,tasks,HandlingTaskOutput

serialization,EloquentSerialization-HidingattributesfromJSON,Glossary

SerializesModelstrait,Creatingajob

servecommand,Artisan,BasicArtisanCommands

server()method,Request,Userandrequeststate

server.phpfile,TheLooseFiles

servicecontainer(seecontainer)

serviceproviders,ServiceProviders-ServiceProviders,ServiceProviders,Glossary

(seealsospecificserviceproviders)

services,injectingintoaview,BladeServiceInjection-BladeServiceInjection

services.phpfile,Mail

sessioncommands,Artisan,TheGroupedCommands

Sessionfacade,AccessingtheSession

session()helper,AccessingtheSession-TheMethodsAvailableonSessionInstances

session()method,Request,AccessingtheSession

session()method,TestCase,AuthenticationandSessions

session.phpfile,Sessions

sessions,Sessions-FlashSessionStorage

Page 761: Laravel: Up and Running: A Framework for Building Modern PHP

accessing,AccessingtheSession-TheMethodsAvailableonSessionInstances

configuring,Sessions

driverssupported,Sessions

flashsessionstorage,FlashSessionStorage

testing,AuthenticationandSessions,Session-Session

setterinjection,AQuickIntroductiontoDependencyInjection

setUp()method,Testing

share()method,Sharingavariableglobally

shellcommands,schedulingastasks,AvailableTaskTypes

ShouldBroadcastinterface,BroadcastinganEvent

shouldHaveReceived()method,Mockery,MockingFacades

shouldIgnoreMissing()method,Mockery,Mockery

shouldReceive()method,Mockery,Mockery,MockingFacades

@showdirective,Blade,DefiningSectionswith@section/@showand@yield-@extends,@sectionand@endsection

show()method,resourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

showLinkRequestForm()method,ForgotPasswordController

showLoginForm()method,AuthenticatesUserstrait

showRegistrationForm()method,RegistersUserstrait

showResetForm()method,ResetPasswordController

shuffle()method,collection,AFewMethods

singleton()method,BindingtoSingletons,Aliases,andInstances

Page 762: Laravel: Up and Running: A Framework for Building Modern PHP

singletons,bindingto,BindingtoSingletons,Aliases,andInstances

size()method,Storage,UsingtheStorageFacade

skip()method,DB,Modifyingmethods

Slacknotifications,Slacknotifications

slash(/),escapinginArtisancommands,ControllerOrganizationandJSONReturns

smallInteger()method,Blueprint,Creatingcolumns

SMSnotifications,SMSnotifications

softdeletes,Softdeletes-Force-deletingsoft-deletedentities,Glossary

softDeletes()method,Blueprint,Creatingcolumns,Enablingsoftdeletes

SoftwareasaService(SaaS),Glossary

sort()method,collection,AFewMethods

sortBy()method,collection,AFewMethods

sortByDesc()method,collection,AFewMethods

sortingAPIresults,SortingandFiltering-SortingYourAPIResults

sourcemaps,Elixir,Sourcemaps

Spark,Glossary

SplFileInfoclass,BasicFileUploadsandManipulation

SQLqueries,raw,RawSQL-Rawdeletes

(seealsoquerybuilder)

SQLite

dependenciesfor,Modifyingcolumns

Page 763: Laravel: Up and Running: A Framework for Building Modern PHP

modifyingmultiplecolumns,Modifyingcolumns

starts_with()helper,TheStringHelpersandPluralization,Strings

statelessAPIs,TheBasicsofREST-LikeJSONAPIs

statement()method,DB,RawSQL

staticcalls,RouteDefinitions

stdClassobject

returnedbyDBfacade,OtherDatabaseConfigurationOptions,Rawselects

returnedbyloops,@forelse

storage,LocalandCloudFileManagers-ConfiguringFileAccess

(seealsodatabases)

additionalproviders,adding,AddingAdditionalFlysystemProviders

cache,Cache-TheMethodsAvailableonCacheInstances

configuring,ConfiguringFileAccess-ConfiguringFileAccess

cookies,Cookies-Settingcookiesonresponseobjects

driverssupported,LocalandCloudFileManagers

Filefacadefor,UsingtheStorageFacade

fileuploads,handling,BasicFileUploadsandManipulation-BasicFileUploadsandManipulation

flashsessionstorage,FlashSessionStorage

sessionstorage,Sessions-FlashSessionStorage

Storagefacademethodsfor,UsingtheStorageFacade-UsingtheStorageFacade

testing,FileStorage-Returningfakefiles

Page 764: Laravel: Up and Running: A Framework for Building Modern PHP

typesof,LocalandCloudFileManagers-ConfiguringFileAccess

storagecommands,Artisan,TheGroupedCommands

Storagefacade,UsingtheStorageFacade-UsingtheStorageFacade

storagefolder,TheFolders

storage:linkcommand,Artisan,ConfiguringFileAccess

storage_path()helper,ConfiguringFileAccess,ApplicationPaths

Storeclass,AccessingtheSession

store()method,resourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

store()method,UploadedFile,UploadedFiles-UploadedFiles,BasicFileUploadsandManipulation

storeAs()method,UploadedFile,UploadedFiles-UploadedFiles,BasicFileUploadsandManipulation

string()method,Blueprint,Creatingcolumns

strings

helpersfor,Strings-Strings

localization,Localization-Pluralizationinlocalization

pluralization,TheStringHelpersandPluralization,Pluralizationinlocalization

stringhelpers,TheStringHelpersandPluralization

str_contains()helper,TheStringHelpersandPluralization,Strings

str_is()helper,TheStringHelpersandPluralization,Strings

str_limit()helper,Strings

str_plural()helper,TheStringHelpersandPluralization

Page 765: Laravel: Up and Running: A Framework for Building Modern PHP

str_random()helper,Strings

str_slug()helper,TheStringHelpersandPluralization,Strings

subdomainrouting,SubdomainRouting

subject()method,mailable,MethodsAvailableinbuild()

submitForm()method,TestCase,ClickingandForms

subscribe()method,events,Eventsubscribers

sum()method,collection,AFewMethods

sum()method,DB,Ending/returningmethods

sum()method,Eloquent,Aggregates

SwiftMailer,Mail

Symfony,Laravel4,Glossary

Consolecomponent,AnIntroductiontoArtisan

HttpFoundationclasses,TheRequestObject

Translationcomponent,Pluralizationinlocalization

sync()method,Eloquent,Manytomany

synchronizertokens,Passport,TokensfromLaravelsessionauthentication(synchronizertokens)-TokensfromLaravelsessionauthentication(synchronizertokens)

systemrequirements,WhoThisBookIsFor,SystemRequirements-Composer

T

table()method,Tableoutput

take()method,collection,AFewMethods

take()method,DB,Modifyingmethods

Page 766: Laravel: Up and Running: A Framework for Building Modern PHP

take()method,Eloquent,RetrievingDatawithEloquent

Task::all()query,Views,Controllers

tasks,scheduling(seescheduler)

templates(seeBlade;views)

@testdocblock,NamingTests

TestCaseclass,TestCase

TestCase.phpfile,TestingBasics

testing,Testing-TestingBasics

APIs,Testing

applicationtests,Testing,ApplicationTesting-AuthenticationandSessions,Glossary

Artisancommands,Testing,ArtisanandSeed

assertionsin,Glossary

authenticationandauthorization,Testing-Testing,AuthenticationandSessions

cache,Cache

cookies,Cookies-Cookies

databaseoperations,Testing-Testing

dependencyinjectionin,Testing

environmentfor,TheTestingEnvironment

errorbags,TestingMessageandErrorBags

failedtestresults,TestingBasics

integrationtests,Testing,Glossary

inversionofcontrolin,Testing

Page 767: Laravel: Up and Running: A Framework for Building Modern PHP

JSON,TestingBasics

localization,TranslationandLocalization

mail,Mail

messagebags,TestingMessageandErrorBags

Mockerylibraryfor,Mocking-MockingFacades

namingtests,NamingTests

notifications,Notifications

requestsandresponses,Testing-Testing

routes,Testing

runningtests,Testing,Tests,TestingwithElixir

seeders,ArtisanandSeed

session,AuthenticationandSessions

sessions,Session-Session

storage,FileStorage-Returningfakefiles

traitsfor,TheTestingTraits

unittests,Testing

unittestsfor,Glossary

userinput,Testing

views,Testing-Testing

withBehat,Testing

withFaker,Testing

Page 768: Laravel: Up and Running: A Framework for Building Modern PHP

withMockery,Testing

withPHPUnit,Testing

writingtests,Testing

testsfolder,TheFolders,TestingBasics,NamingTests

text()method,Blueprint,Creatingcolumns

text()method,mailable,MailTemplates

thenPing()method,tasks,HandlingTaskOutput

time()method,Blueprint,Creatingcolumns

timesanddates(seeCarbonpackage;scheduler;timestamps)

timestamp()method,Blueprint,Creatingcolumns

timestamps,Creatingcolumns,Timestamps,Datemutators,ChildRecordsUpdatingParentRecordTimestamps-Eagerloadingonlythecount

timestamps()method,Blueprint,Creatingcolumns

Tinker,BasicArtisanCommands,Tinker,Tinker,Glossary

tinkercommand,Artisan,BasicArtisanCommands

tinyInteger()method,Blueprint,Creatingcolumns

TL;DR(toolong;didn’tread),HowThisBookIsStructured,Glossary

to()method,redirect()->to()

toArray()method,collection,AFewMethods

toArray()method,Eloquent,EloquentSerialization

toBroadcast()method,notification,Broadcastnotifications

toDatabase()method,notification,Databasenotifications

Page 769: Laravel: Up and Running: A Framework for Building Modern PHP

toJson()method,Eloquent,EloquentSerialization

TokenizerPHPextension,SystemRequirements

tokens,CSRF,CSRFProtection-CSRFProtection

toMail()method,notification,Emailnotifications-Emailnotifications

toNexmo()method,notification,SMSnotifications

toolong;didn’tread(TL;DR),HowThisBookIsStructured,Glossary

toOthers()method,events,Excludingthecurrentuserfrombroadcastevents,Excludingthecurrentuser

top-leveldomains,forlocaldevelopmentsite,ConfiguringHomestead

toSearchableArray()method,MarkingYourModelforIndexing

toSlack()method,notification,Slacknotifications

transaction()method,DB,Transactions

transactions,Transactions

translation(seelocalization)

Translationcomponent,Symfony,Pluralizationinlocalization

trashed()method,Eloquent,Queryingwithsoftdeletes

truncate()method,DB,Deletes

TwigBridgepackage,BladeTemplating

(seealsoBlade)

type()method,TestCase,ClickingandForms

typehint,InjectingDependenciesintoControllers,Glossary

typehinting,HowtheContainerIsWired,BindingaConcreteInstancetoanInterface

Page 770: Laravel: Up and Running: A Framework for Building Modern PHP

U

uncheck()method,TestCase,ClickingandForms

union()method,DB,Unions

unionAll()method,DB,Unions

unique()method,Blueprint,Buildingextrapropertiesfluently

unittests,Testing,Glossary

universalto,formail,Universalto

@unlessdirective,Blade,@unlessand@endunless

unsearchable()method,ManuallyTriggerIndexingviaCode

unsigned()method,Blueprint,Buildingextrapropertiesfluently

up()method,migrations,DefiningMigrations,DefiningMigrations

update()method,DB,Rawupdates,Updates

update()method,Eloquent,Updates,Massassignment

update()method,resourcecontrollers,ThemethodsofLaravel’sresourcecontrollers

updateExistingPivot()method,Eloquent,Manytomany

uploadedfiles,UploadedFiles-UploadedFiles,BasicFileUploadsandManipulation-BasicFileUploadsandManipulation,FileStorage-Returningfakefiles

UploadedFileclass,UploadedFiles,Files,Uploadingfakefiles

url()helper,RouteNames,RouteNames,URLs

url()method,Request,Userandrequeststate

URLs

helpersfor,URLs-URLs

Page 771: Laravel: Up and Running: A Framework for Building Modern PHP

userinputfromrouteparameters,FromRouteParameters

userinputfromURLsegments,FromRequest

userauthentication(seeauthentication)

userauthorization(seeauthorization)

userinput

Artisancommands,UsingInput-Prompts

Eloquentmodel,EloquentModelMassAssignment

formrequests,FormRequests-UsingaFormRequest

gettingandhandlingwithcontrollers,GettingUserInput-GettingUserInput

Requestobject,InjectingaRequestObject-JSONInput(and$request->json()),Basicuserinput-Userandrequeststate

routeparameters,FromRouteParameters

testing,Testing

uploadedfiles,UploadedFiles-UploadedFiles

URLs,RouteData

validating,Validation-DisplayingValidationErrorMessages

Usermodel,TheUserModelandMigration-TheUserModelandMigration

user()method,Usingtheauth()GlobalHelperandtheAuthFacade,ChangingtheDefaultGuard

username()method,AuthenticatesUserstrait

uuid()method,Blueprint,Creatingcolumns

V

Vagrant,InstallingHomestead’sdependencies,Glossary

Page 772: Laravel: Up and Running: A Framework for Building Modern PHP

commandsfor,UsingHomesteadday-to-day

mappingHomesteadfoldersto,ConfiguringHomestead

migrationswith,RunningMigrations

Valetpackage,LaravelValet-LaravelValet,Glossary

validate()method,controller,redirect()->with(),validate()intheControllerUsingValidatesRequests-validate()intheControllerUsingValidatesRequests

validateLogin()method,AuthenticatesUserstrait

validationofuserinput,Validation-DisplayingValidationErrorMessages,Glossary

errormessagesfrom,displaying,DisplayingValidationErrorMessages

manualvalidation,ManualValidation

validate()method,controller,validate()intheControllerUsingValidatesRequests-validate()intheControllerUsingValidatesRequests

validationrules,validate()intheControllerUsingValidatesRequests

Validatorclass,MessageBags,ManualValidation

validator()method,RegisterController

vendorcommands,Artisan,TheGroupedCommands

vendorfolder,TheFolders

vendor:publishcommand,Artisan,TheVuecomponents,InstallingScout

versioning,inElixir,Versioning-Versioning

versionsofLaravel,HowThisBookIsStructured

versionsofLaravel,priorto5.2

ACL(accesscontrollist),Authorization(ACL)andRoles

authenticationguards,ChangingtheDefaultGuard

Page 773: Laravel: Up and Running: A Framework for Building Modern PHP

fluentroutedefinitions,RouteNames

middlewaregroups,Usingmiddlewaregroups

render()method,pagination,PaginatingDatabaseResults

testingtraits,TestCase

versionsofLaravel,priorto5.3

APItokenauthentication,Laravel5.2+APITokenAuthentication-Laravel5.2+APITokenAuthentication

assertViewHas()method,Testing

authenticationcontrollers,UserAuthenticationandAuthorization

classicmail,“Classic”Mail

compilingJavaScript,ProcessingJavaScript

DBfacaderesults,Rawselects

Eloquentresults,Getmany

$expressionparameter,ParametersinCustomBladeDirectives

generatingresourcecontrollers,Controllers

$loopvariable,@forelse

PHPandextensions,SystemRequirements

policymethods,Generatingpolicies

routesfile,RouteDefinitions

withCookie()method,Response,Settingcookiesonresponseobjects

via()method,notification,Notifications,Definingthevia()MethodforYourNotifiables

viaRemember()method,“RememberMe”

Page 774: Laravel: Up and Running: A Framework for Building Modern PHP

viewcommands,Artisan,TheGroupedCommands

viewcomposers,UsingViewComposerstoShareVariableswithEveryView,BindingDatatoViewsUsingViewComposers-Class-basedviewcomposers,Glossary

viewresponses,Viewresponses

view()helper,ViewComposersandServiceInjection-Class-basedviewcomposers,Viewresponses,Misc

view()method,Response,Viewresponses

views,Views-UsingViewComposerstoShareVariableswithEveryView,Glossary,Glossary

bindingdatato,BindingDatatoViewsUsingViewComposers-Class-basedviewcomposers

loading,Views

passingvariablesto,Views,ViewComposersandServiceInjection

testing,Testing-Testing

typesof,Views

VirtualBox,InstallingHomestead’sdependencies

$visibleproperty,TransformingResults

visit()method,TestCase,“Visiting”Routes

VMWare,InstallingHomestead’sdependencies

Vuecomponents,TheVuecomponents-TheVuecomponents

W

wantsJson()method,Request,Userandrequeststate

webguard,Guards

webmiddlewaregroup,Usingmiddlewaregroups

Page 775: Laravel: Up and Running: A Framework for Building Modern PHP

webroutes,RouteDefinitions

(seealsoroutes)

web.phpfile,RouteDefinitions

Webpack,ProcessingJavaScript

websiteresources(seeonlineresources)

WebSocketauthentication(seeEcho)

WebSockets,BroadcastingEventsoverWebSockets,andLaravelEcho-SubscribingtonotificationswithEcho

authorizationforchannels,BindingauthorizationdefinitionsforWebSocketchannels-BindingauthorizationdefinitionsforWebSocketchannels

broadcastingevents,BroadcastinganEvent-BroadcastinganEvent

channelsfor,BroadcastinganEvent,BindingauthorizationdefinitionsforWebSocketchannels

configuring,ConfigurationandSetup

driverssupported,ConfigurationandSetup

Echofor,ReceivingtheMessage

eventstructurefor,BroadcastinganEvent

excludinguserfromevents,Excludingthecurrentuserfrombroadcastevents-Excludingthecurrentuserfrombroadcastevents

Pub/Subpatternusedby,BroadcastingEventsoverWebSockets,andLaravelEcho

queueworkerfor,ConfigurationandSetup

receivingeventmessages,ReceivingtheMessage-ReceivingtheMessage

serviceproviderconfiguration,Thebroadcastserviceprovider

Page 776: Laravel: Up and Running: A Framework for Building Modern PHP

when()method,ContextualBinding

where()method,collection,AFewMethods

where()method,DB,Constrainingmethods-Constrainingmethods

where()method,Eloquent,RetrievingDatawithEloquent,FilteringYourAPIResults

whereBetween()method,DB,Constrainingmethods

whereExists()method,DB,Constrainingmethods

whereIn()method,DB,Constrainingmethods

whereNull()method,DB,Constrainingmethods

whereRaw()method,DB,Constrainingmethods

with()method,redirect()->with()-redirect()->with(),Closure-basedviewcomposers

withCookie()method,Response,Settingcookiesonresponseobjects

withErrors()method,NamedErrorBags

withInput()method,redirect()->with()

withoutEvents()method,TestCase,JobsandEvents

withoutGlobalScope()method,Removingglobalscopes

withoutGlobalScopes()method,Removingglobalscopes

WithoutMiddlewaretrait,WithoutMiddleware

withoutOverlapping()method,tasks,BlockingandOverlap

withoutSyncingToSearch()method,PerformOperationsWithoutIndexing

withPivot()method,Eloquent,Gettingdatafromthepivottable

withSwiftMessage()method,mailable,MethodsAvailableinbuild()

Page 777: Laravel: Up and Running: A Framework for Building Modern PHP

withTrashed()method,Eloquent,Queryingwithsoftdeletes

workersforqueues,RunningaQueueWorker,ConfigurationandSetup

X

X-precedingheadernames,ReadingandSendingHeaders

Y

@yielddirective,Blade,DefiningSectionswith@section/@showand@yield

Page 778: Laravel: Up and Running: A Framework for Building Modern PHP

AbouttheAuthorMattStaufferisadeveloperandateacher.HeisapartnerandtechnicaldirectoratTightenCo.,blogsatmattstauffer.co,andhostsTheFive-MinuteGeekShowandtheLaravelPodcast.

Page 779: Laravel: Up and Running: A Framework for Building Modern PHP

ColophonTheanimalonthecoverofLaravel:UpandRunningisagemsbok(oryxgazella).ThislargeantelopeisnativetothedesertsofSouthAfrica,Botswana,Zimbabwe,andNamibia,whereitisfeaturedonthecountry’scoatofarms.

Gemsbokmeasureabout5feet7inchestallattheshoulderandcanweighfrom250to390pounds.Theyaretypicallypalegrayorbrown,withblackandwhitefacialmarkingsandlongblacktails.Ablackstripeextendsfromthechintotheloweredgeoftheneck.Thegemsbok’simpressivestraighthorns,usedindefensivemaneuvers,average33inchesinlengthandareregardedascharmsinmanycultures.InmedievalEngland,theywereoftenmarketedasunicornhorns.

Althoughthesehornsmakethegemsbokahighly-soughttrophyanimal,thepopulationremainsstablethroughoutSouthernAfrica.In1969,gemsbokwereintroducedtosouthernNewMexico,wheretheircurrentpopulationisaround3,000.

Gemsbokarewell-suitedtosuchdesertenvironments,withtheabilitytosurvivewithoutdrinkingwaterformostoftheyear.Toachievethis,theydonotpantorsweat,allowingtheirbodytemperaturetoriseseveraldegreesabovenormalonhotdays.Theirlifespanisapproximately18yearsinthewild.

ManyoftheanimalsonO’Reillycoversareendangered;allofthemareimportanttotheworld.Tolearnmoreabouthowyoucanhelp,gotoanimals.oreilly.com.

ThecoverimageisfromRiversideNaturalHistory.ThecoverfontsareURWTypewriterandGuardianSans.ThetextfontisAdobeMinionPro;theheadingfontisAdobeMyriadCondensed;andthecodefontisDaltonMaag’sUbuntuMono.

Page 780: Laravel: Up and Running: A Framework for Building Modern PHP

PrefaceWhatThisBookIsAbout

WhoThisBookIsFor

HowThisBookIsStructured

ConventionsUsedinThisBook

O’ReillySafari

HowtoContactUs

Acknowledgments

1.WhyLaravel?WhyUseaFramework?

“I’llJustBuildItMyself”

ConsistencyandFlexibility

AShortHistoryofWebandPHPFrameworksRubyonRails

TheInfluxofPHPFrameworks

TheGoodandtheBadofCodeIgniter

Laravel1,2,and3

Laravel4

Laravel5

What’sSoSpecialAboutLaravel?ThePhilosophyofLaravel

HowLaravelAchievesDeveloperHappiness

TheLaravelCommunity

HowItWorks

WhyLaravel?

Page 781: Laravel: Up and Running: A Framework for Building Modern PHP

2.SettingUpaLaravelDevelopmentEnvironmentSystemRequirements

Composer

LocalDevelopmentEnvironmentsLaravelValet

LaravelHomestead

CreatingaNewLaravelProjectInstallingLaravelwiththeLaravelInstallerTool

InstallingLaravelwithComposer ’screate-projectFeature

Laravel’sDirectoryStructureTheFolders

TheLooseFiles

Configuration

UpandRunning

Testing

TL;DR

3.RoutingandControllersRouteDefinitions

RouteVerbs

RouteHandling

RouteParameters

RouteNames

RouteGroupsMiddleware

PathPrefixes

SubdomainRouting

Page 782: Laravel: Up and Running: A Framework for Building Modern PHP

NamespacePrefixes

NamePrefixes

ViewsUsingViewComposerstoShareVariableswithEveryView

ControllersGettingUserInput

InjectingDependenciesintoControllers

ResourceControllers

RouteModelBindingImplicitRouteModelBinding

CustomRouteModelBinding

RouteCaching

FormMethodSpoofingAnIntroductiontoHTTPVerbs

HTTPVerbsinLaravel

HTTPMethodSpoofinginHTMLForms

CSRFProtection

Redirectsredirect()->to()

redirect()->route()

redirect()->back()

OtherRedirectMethods

redirect()->with()

AbortingtheRequest

CustomResponses

Page 783: Laravel: Up and Running: A Framework for Building Modern PHP

response()->make()

response()->json()and->jsonp()

response()->download()and->file()

Testing

TL;DR

4.BladeTemplatingEchoingData

ControlStructuresConditionals

Loops

or

TemplateInheritanceDefiningSectionswith@section/@showand@yield

@parent

@include

@each

ViewComposersandServiceInjectionBindingDatatoViewsUsingViewComposers

BladeServiceInjection

CustomBladeDirectivesParametersinCustomBladeDirectives

Example:UsingCustomBladeDirectivesforaMultitenantApp

Testing

TL;DR

5.FrontendComponents

Page 784: Laravel: Up and Running: A Framework for Building Modern PHP

ElixirElixirFolderStructure

RunningElixir

WhatDoesElixirProvide?

PaginationPaginatingDatabaseResults

ManuallyCreatingPaginators

MessageBagsNamedErrorBags

StringHelpers,Pluralization,andLocalizationTheStringHelpersandPluralization

Localization

TestingTestingwithElixir

TestingMessageandErrorBags

TranslationandLocalization

TL;DR

6.CollectingandHandlingUserDataInjectingaRequestObject

$request->all()

$request->except()and$request->only()

$request->has()and$request->exists()

$request->input()

ArrayInput

JSONInput(and$request->json())

RouteData

Page 785: Laravel: Up and Running: A Framework for Building Modern PHP

FromRequest

FromRouteParameters

UploadedFiles

Validationvalidate()intheControllerUsingValidatesRequests

ManualValidation

DisplayingValidationErrorMessages

FormRequestsCreatingaFormRequest

UsingaFormRequest

EloquentModelMassAssignment

{{Versus{!!

Testing

TL;DR

7.ArtisanandTinkerAnIntroductiontoArtisan

BasicArtisanCommandsOptions

TheGroupedCommands

WritingCustomArtisanCommandsRegisteringCommands

ASampleCommand

ArgumentsandOptions

UsingInput

Prompts

Page 786: Laravel: Up and Running: A Framework for Building Modern PHP

Output

CallingArtisanCommandsinNormalCode

Tinker

Testing

TL;DR

8.DatabaseandEloquentConfiguration

DatabaseConnections

OtherDatabaseConfigurationOptions

MigrationsDefiningMigrations

RunningMigrations

SeedingCreatingaSeeder

ModelFactories

QueryBuilderBasicUsageoftheDBFacade

RawSQL

ChainingwiththeQueryBuilder

Transactions

IntroductiontoEloquentCreatingandDefiningEloquentModels

RetrievingDatawithEloquent

InsertsandUpdateswithEloquent

DeletingwithEloquent

Page 787: Laravel: Up and Running: A Framework for Building Modern PHP

Scopes

CustomizingFieldInteractionswithAccessors,Mutators,andAttributeCasting

EloquentCollections

EloquentSerialization

EloquentRelationships

ChildRecordsUpdatingParentRecordTimestamps

EloquentEvents

Testing

TL;DR

9.UserAuthenticationandAuthorizationTheUserModelandMigration

Usingtheauth()GlobalHelperandtheAuthFacade

TheAuthControllersRegisterController

LoginController

ResetPasswordController

ForgotPasswordController

Auth::routes()

TheAuthScaffold

“RememberMe”

ManuallyAuthenticatingUsers

AuthMiddleware

GuardsChangingtheDefaultGuard

Page 788: Laravel: Up and Running: A Framework for Building Modern PHP

UsingOtherGuardsWithoutChangingtheDefault

AddingaNewGuard

CreatingaCustomUserProvider

CustomUserProvidersforNonrelationalDatabases

AuthEvents

Authorization(ACL)andRolesDefiningAuthorizationRules

TheGateFacade(andInjectingGate)

TheAuthorizeMiddleware

ControllerAuthorization

CheckingontheUserInstance

BladeChecks

InterceptingChecks

Policies

Testing

TL;DR

10.RequestsandResponsesLaravel’sRequestLifecycle

BootstrappingtheApplication

ServiceProviders

TheRequestObjectGettingaRequestObjectinLaravel

GettingBasicInformationAboutaRequest

Persistence

TheResponseObject

Page 789: Laravel: Up and Running: A Framework for Building Modern PHP

UsingandCreatingResponseObjectsinControllers

SpecializedResponseTypes

LaravelandMiddlewareAnIntroductiontoMiddleware

CreatingCustomMiddleware

BindingMiddleware

PassingParameterstoMiddleware

Testing

TL;DR

11.TheContainerAQuickIntroductiontoDependencyInjection

DependencyInjectionandLaravel

Theapp()GlobalHelper

HowtheContainerIsWired

BindingClassestotheContainerBindingtoaClosure

BindingtoSingletons,Aliases,andInstances

BindingaConcreteInstancetoanInterface

ContextualBinding

ConstructorInjection

MethodInjection

FacadesandtheContainerHowFacadesWork

ServiceProviders

Testing

Page 790: Laravel: Up and Running: A Framework for Building Modern PHP

TL;DR

12.TestingTestingBasics

NamingTests

TheTestingEnvironment

TheTestingTraitsWithoutMiddleware

DatabaseMigrations

DatabaseTransactions

ApplicationTestingTestCase

“Visiting”Routes

CustomApplicationTestingAssertions

JSONandNon-visit()ApplicationTestingAssertions

ClickingandForms

JobsandEvents

AuthenticationandSessions

ArtisanandSeed

MockingMockery

MockingFacades

TL;DR

13.WritingAPIsTheBasicsofREST-LikeJSONAPIs

ControllerOrganizationandJSONReturns

Page 791: Laravel: Up and Running: A Framework for Building Modern PHP

ReadingandSendingHeadersSendingResponseHeadersinLaravel

ReadingRequestHeadersinLaravel

EloquentPagination

SortingandFilteringSortingYourAPIResults

FilteringYourAPIResults

TransformingResultsWritingYourOwnTransformer

NestingandRelationships

APIAuthenticationwithLaravelPassportABriefIntroductiontoOAuth2.0

InstallingPassport

Passport’sAPI

Passport’sAvailableGrantTypes

ManagingClientsandTokenswiththePassportAPIandtheVueComponents

PassportScopes

Laravel5.2+APITokenAuthentication

Testing

TL;DR

14.StorageandRetrievalLocalandCloudFileManagers

ConfiguringFileAccess

UsingtheStorageFacade

AddingAdditionalFlysystemProviders

Page 792: Laravel: Up and Running: A Framework for Building Modern PHP

BasicFileUploadsandManipulation

SessionsAccessingtheSession

TheMethodsAvailableonSessionInstances

FlashSessionStorage

CacheAccessingtheCache

TheMethodsAvailableonCacheInstances

CookiesCookiesinLaravel

AccessingtheCookieTools

Full-TextSearchwithLaravelScoutInstallingScout

MarkingYourModelforIndexing

SearchingYourIndex

QueuesandScout

PerformOperationsWithoutIndexing

ManuallyTriggerIndexingviaCode

ManuallyTriggerIndexingviatheCLI

TestingFileStorage

Session

Cache

Cookies

TL;DR

Page 793: Laravel: Up and Running: A Framework for Building Modern PHP

15.MailandNotificationsMail

“Classic”Mail

Basic“Mailable”MailUsage

MailTemplates

MethodsAvailableinbuild()

AttachmentsandInlineImages

Queues

LocalDevelopment

NotificationsDefiningthevia()MethodforYourNotifiables

SendingNotifications

QueueingNotifications

Out-of-the-BoxNotificationTypes

TestingMail

Notifications

TL;DR

16.Queues,Jobs,Events,Broadcasting,andtheSchedulerQueues

WhyQueues?

BasicQueueConfiguration

QueuedJobs

RunningaQueueWorker

HandlingErrors

Page 794: Laravel: Up and Running: A Framework for Building Modern PHP

ControllingtheQueue

QueuesSupportingOtherFunctions

EventsFiringanEvent

ListeningforanEvent

BroadcastingEventsoverWebSockets,andLaravelEchoConfigurationandSetup

BroadcastinganEvent

ReceivingtheMessage

AdvancedBroadcastingTools

LaravelEcho(theJavaScriptSide)

SchedulerAvailableTaskTypes

AvailableTimeFrames

BlockingandOverlap

HandlingTaskOutput

TaskHooks

Testing

TL;DR

17.HelpersandCollectionsHelpers

Arrays

Strings

ApplicationPaths

URLs

Page 795: Laravel: Up and Running: A Framework for Building Modern PHP

Misc

CollectionsTheBasicsofCollections

AFewMethods

TL;DR

Glossary

Index