配色: 字号:
控制器(Controller)Symfony
2016-08-18 | 阅:  转:  |  分享 
  
控制器(Controller)

AcontrollerisaPHPcallableyoucreatethattakesinformationfromtheHTTPrequestandconstructsandreturnsanHTTPresponse(asaSymfonyResponseobject).TheresponsecouldbeanHTMLpage,anXMLdocument,aserializedJSONarray,animage,aredirect,a404errororanythingelseyoucandreamup.Thecontrollercontainswhateverarbitrarylogicyourapplicationneedstorenderthecontentofapage.



SeehowsimplethisisbylookingataSymfonycontrollerinaction.ThisrendersapagethatprintsthefamousHelloworld!:



useSymfony\Component\HttpFoundation\Response;



publicfunctionhelloAction()

{

returnnewResponse(''Helloworld!'');

}

Thegoalofacontrollerisalwaysthesame:createandreturnaResponseobject.Alongtheway,itmightreadinformationfromtherequest,loadadatabaseresource,sendanemail,orsetinformationontheuser’ssession.Butinallcases,thecontrollerwilleventuallyreturntheResponseobjectthatwillbedeliveredbacktotheclient.



There’snomagicandnootherrequirementstoworryabout!Hereareafewcommonexamples:



ControllerApreparesaResponseobjectrepresentingthecontentforthehomepageofthesite.

ControllerBreadstheslugparameterfromtherequesttoloadablogentryfromthedatabaseandcreateaResponseobjectdisplayingthatblog.Iftheslugcan’tbefoundinthedatabase,itcreatesandreturnsaResponseobjectwitha404statuscode.

ControllerChandlestheformsubmissionofacontactform.Itreadstheforminformationfromtherequest,savesthecontactinformationtothedatabaseandemailsthecontactinformationtoyou.Finally,itcreatesaResponseobjectthatredirectstheclient’sbrowsertothecontactform“thankyou”page.

Requests->Controller->Response的过程

EveryrequesthandledbyaSymfonyprojectgoesthroughthesamesimplelifecycle.Theframeworktakescareofalltherepetitivestuff:youjustneedtowriteyourcustomcodeinthecontrollerfunction:



Eachrequestishandledbyasinglefrontcontrollerfile(e.g.app.phporapp_dev.php)thatbootstrapstheapplication;

TheRouterreadsinformationfromtherequest(e.g.theURI),findsaroutethatmatchesthatinformation,andreadsthe_controllerparameterfromtheroute;

ThecontrollerfromthematchedrouteisexecutedandthecodeinsidethecontrollercreatesandreturnsaResponseobject;

TheHTTPheadersandcontentoftheResponseobjectaresentbacktotheclient.

Creatingapageisaseasyascreatingacontroller(#3)andmakingaroutethatmapsaURLtothatcontroller(#2).



注解



Thoughsimilarlynamed,a“frontcontroller”isdifferentfromthe“controllers”talkedaboutinthischapter.AfrontcontrollerisashortPHPfilethatlivesinyourwebdirectoryandthroughwhichallrequestsaredirected.Atypicalapplicationwillhaveaproductionfrontcontroller(e.g.app.php)andadevelopmentfrontcontroller(e.g.app_dev.php).You’lllikelyneverneedtoedit,vieworworryaboutthefrontcontrollersinyourapplication.

简单示例

WhileacontrollercanbeanyPHPcallable(afunction,methodonanobject,oraClosure),acontrollerisusuallyamethodinsideacontrollerclass.Controllersarealsocalledactions.



//src/AppBundle/Controller/HelloController.php

namespaceAppBundle\Controller;



useSymfony\Component\HttpFoundation\Response;



classHelloController

{

publicfunctionindexAction($name)

{

returnnewResponse(''Hello''.$name.''!'');

}

}

小技巧



NotethatthecontrolleristheindexActionmethod,whichlivesinsideacontrollerclass(HelloController).Don’tbeconfusedbythenaming:acontrollerclassissimplyaconvenientwaytogroupseveralcontrollers/actionstogether.Typically,thecontrollerclasswillhouseseveralcontrollers/actions(e.g.updateAction,deleteAction,etc).

Thiscontrollerisprettystraightforward:



line4:SymfonytakesadvantageofPHP’snamespacefunctionalitytonamespacetheentirecontrollerclass.TheusekeywordimportstheResponseclass,whichthecontrollermustreturn.

line6:Theclassnameistheconcatenationofanameforthecontrollerclass(i.e.Hello)andthewordController.Thisisaconventionthatprovidesconsistencytocontrollersandallowsthemtobereferencedonlybythefirstpartofthename(i.e.Hello)intheroutingconfiguration.

line8:EachactioninacontrollerclassissuffixedwithActionandisreferencedintheroutingconfigurationbytheaction’sname(index).Inthenextsection,you’llcreatearoutethatmapsaURItothisaction.You’lllearnhowtheroute’splaceholders({name})becomeargumentstotheactionmethod($name).

line10:ThecontrollercreatesandreturnsaResponseobject.

创建URL->Controller的映射关系

ThenewcontrollerreturnsasimpleHTMLpage.Toactuallyviewthispageinyourbrowser,youneedtocreatearoute,whichmapsaspecificURLpathtothecontroller:



Annotations

//src/AppBundle/Controller/HelloController.php

namespaceAppBundle\Controller;



useSymfony\Component\HttpFoundation\Response;

useSensio\Bundle\FrameworkExtraBundle\Configuration\Route;



classHelloController

{

/

@Route("/hello/{name}",name="hello")

/

publicfunctionindexAction($name)

{

returnnewResponse(''Hello''.$name.''!'');

}

}

YAML

#app/config/routing.yml

hello:

path:/hello/{name}

#usesaspecialsyntaxtopointtothecontroller-seenotebelow

defaults:{_controller:AppBundle:Hello:index}

XML






xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://symfony.com/schema/routing

http://symfony.com/schema/routing/routing-1.0.xsd">







AppBundle:Hello:index





PHP

//app/config/routing.php

useSymfony\Component\Routing\Route;

useSymfony\Component\Routing\RouteCollection;



$collection=newRouteCollection();

$collection->add(''hello'',newRoute(''/hello/{name}'',array(

//usesaspecialsyntaxtopointtothecontroller-seenotebelow

''_controller''=>''AppBundle:Hello:index'',

)));



return$collection;

Now,youcangoto/hello/ryan(e.g.http://localhost:8000/app_dev.php/hello/ryanifyou’reusingthebuilt-inwebserver)andSymfonywillexecutetheHelloController::indexAction()controllerandpassinryanforthe$namevariable.Creatinga“page”meanssimplycreatingacontrollermethodandanassociatedroute.



Simple,right?



TheAppBundle:Hello:indexcontrollersyntax



IfyouusetheYMLorXMLformats,you’llrefertothecontrollerusingaspecialshortcutsyntax:AppBundle:Hello:index.Formoredetailsonthecontrollerformat,seeController命名规则.

参见



YoucanlearnmuchmoreabouttheroutingsystemintheRoutingchapter.

使用Route参数作为Controller参数

YoualreadyknowthattheroutepointstotheHelloController::indexAction()methodthatlivesinsideAppBundle.What’smoreinterestingistheargumentthatispassedtothatmethod:



//src/AppBundle/Controller/HelloController.php

//...

useSensio\Bundle\FrameworkExtraBundle\Configuration\Route;



/

@Route("/hello/{name}",name="hello")

/

publicfunctionindexAction($name)

{

//...

}

Thecontrollerhasasingleargument,$name,whichcorrespondstothe{name}parameterfromthematchedroute(ryanifyougoto/hello/ryan).Whenexecutingyourcontroller,Symfonymatcheseachargumentwithaparameterfromtheroute.Sothevaluefor{name}ispassedto$name.



Takethefollowingmore-interestingexample:



Annotations

//src/AppBundle/Controller/HelloController.php

//...



useSensio\Bundle\FrameworkExtraBundle\Configuration\Route;



classHelloController

{

/

@Route("/hello/{firstName}/{lastName}",name="hello")

/

publicfunctionindexAction($firstName,$lastName)

{

//...

}

}

YAML

#app/config/routing.yml

hello:

path:/hello/{firstName}/{lastName}

defaults:{_controller:AppBundle:Hello:index}

XML






xmlns:xsi="http:/www.wang027.com/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.baiyuewang.ne/schema/routing

http://www.wang027.com/schema/routing/routing-1.0.xsd">





AppBundle:Hello:index





PHP

//app/config/routing.php

useSymfony\Component\Routing\Route;

useSymfony\Component\Routing\RouteCollection;



$collection=newRouteCollection();

$collection->add(''hello'',newRoute(''/hello/{firstName}/{lastName}'',array(

''_controller''=>''AppBundle:Hello:index'',

)));



return$collection;

Now,thecontrollercanhavetwoarguments:



publicfunctionindexAction($firstName,$lastName)

{

//...

}

Mappingrouteparameterstocontrollerargumentsiseasyandflexible.Keepthefollowingguidelinesinmindwhileyoudevelop.



Theorderofthecontrollerargumentsdoesnotmatter



Symfonymatchestheparameternamesfromtheroutetothevariablenamesofthecontroller.Theargumentsofthecontrollercouldbetotallyreorderedandstillworkperfectly:



publicfunctionindexAction($lastName,$firstName)

{

//...

}

Eachrequiredcontrollerargumentmustmatchupwitharoutingparameter



ThefollowingwouldthrowaRuntimeExceptionbecausethereisnofooparameterdefinedintheroute:



publicfunctionindexAction($firstName,$lastName,$foo)

{

//...

}

Makingtheargumentoptional,however,isperfectlyok.Thefollowingexamplewouldnotthrowanexception:



publicfunctionindexAction($firstName,$lastName,$foo=''bar'')

{

//...

}

Notallroutingparametersneedtobeargumentsonyourcontroller



If,forexample,thelastNameweren’timportantforyourcontroller,youcouldomititentirely:



publicfunctionindexAction($firstName)

{

//...

}

小技巧



Everyroutealsohasaspecial_routeparameter,whichisequaltothenameoftheroutethatwasmatched(e.g.hello).Thoughnotusuallyuseful,thisisalsoavailableasacontrollerargument.Youcanalsopassothervariablesfromyourroutetoyourcontrollerarguments.SeeHowtoPassExtraInformationfromaRoutetoaController.

使用Request对象作为Controller参数

Whatifyouneedtoreadqueryparameters,grabarequestheaderorgetaccesstoanuploadedfile?AllofthatinformationisstoredinSymfony’sRequestobject.Togetitinyourcontroller,justadditasanargumentandtype-hintitwiththeRequestclass:



useSymfony\Component\HttpFoundation\Request;



publicfunctionindexAction($firstName,$lastName,Request$request)

{

$page=$request->query->get(''page'',1);



//...

}

参见



Wanttoknowmoreaboutgettinginformationfromtherequest?SeeAccessRequestInformation.

Controller基类

Forconvenience,SymfonycomeswithanoptionalbaseControllerclass.Ifyouextendit,you’llgetaccesstoanumberofhelpermethodsandallofyourserviceobjectsviathecontainer(see访问其他Services).



AddtheusestatementatoptheControllerclassandthenmodifytheHelloControllertoextendit:



//src/AppBundle/Controller/HelloController.php

namespaceAppBundle\Controller;



useSymfony\Bundle\FrameworkBundle\Controller\Controller;



classHelloControllerextendsController

{

//...

}

Thisdoesn’tactuallychangeanythingabouthowyourcontrollerworks:itjustgivesyouaccesstohelpermethodsthatthebasecontrollerclassmakesavailable.ThesearejustshortcutstousingcoreSymfonyfunctionalitythat’savailabletoyouwithorwithouttheuseofthebaseControllerclass.AgreatwaytoseethecorefunctionalityinactionistolookintheControllerclass.



参见



Ifyou’recuriousabouthowacontrollerwouldworkthatdidnotextendthisbaseclass,checkoutControllersasServices.Thisisoptional,butcangiveyoumorecontrolovertheexactobjects/dependenciesthatareinjectedintoyourcontroller.

Redirecting

Ifyouwanttoredirecttheusertoanotherpage,usetheredirectToRoute()method:



publicfunctionindexAction()

{

return$this->redirectToRoute(''homepage'');



//redirectToRouteisequivalenttousingredirect()andgenerateUrl()together:

//return$this->redirect($this->generateUrl(''homepage''),301);

}

2.6新版功能:TheredirectToRoute()methodwasaddedinSymfony2.6.Previously(andstillnow),youcoulduseredirect()andgenerateUrl()togetherforthis(seetheexampleabove).



Or,ifyouwanttoredirectexternally,justuseredirect()andpassittheURL:



publicfunctionindexAction()

{

return$this->redirect(''http://symfony.com/doc'');

}

Bydefault,theredirectToRoute()methodperformsa302(temporary)redirect.Toperforma301(permanent)redirect,modifythethirdargument:



publicfunctionindexAction()

{

return$this->redirectToRoute(''homepage'',array(),301);

}

小技巧



TheredirectToRoute()methodissimplyashortcutthatcreatesaResponseobjectthatspecializesinredirectingtheuser.It’sequivalentto:



useSymfony\Component\HttpFoundation\RedirectResponse;



publicfunctionindexAction()

{

returnnewRedirectResponse($this->generateUrl(''homepage''));

}

模板渲染

Ifyou’reservingHTML,you’llwanttorenderatemplate.Therender()methodrendersatemplateandputsthatcontentintoaResponseobjectforyou:



//rendersapp/Resources/views/hello/index.html.twig

return$this->render(''hello/index.html.twig'',array(''name''=>$name));

Youcanalsoputtemplatesindeepersub-directories.Justtrytoavoidcreatingunnecessarilydeepstructures:

//rendersapp/Resources/http://www.wang027.com/greetings/index.html.twig

return$this->render(''hello/greetings/index.html.twig'',array(''name''=>$name));

TheSymfonytemplatingengineisexplainedingreatdetailintheTemplatingchapter.



ReferencingTemplatesthatLiveinsidetheBundle



YoucanalsoputtemplatesintheResources/viewsdirectoryofabundleandreferencethemwithaBundleName:DirectoryName:FileNamesyntax.Forexample,AppBundle:Hello:index.html.twigwouldrefertothetemplatelocatedinsrc/AppBundle/Resources/views/Hello/index.html.twig.See引用Bundle里的模板.

访问其他Services

Symfonycomespackedwithalotofusefulobjects,calledservices.Theseareusedforrenderingtemplates,sendingemails,queryingthedatabaseandanyother“work”youcanthinkof.Whenyouinstallanewbundle,itprobablybringsinevenmoreservices.



Whenextendingthebasecontrollerclass,youcanaccessanySymfonyserviceviatheget()method.Hereareseveralcommonservicesyoumightneed:



$templating=$this->get(''templating'');



$router=$this->get(''router'');



$mailer=$this->get(''mailer'');

Whatotherservicesexist?Youcanlistallservices,usethedebug:containerconsolecommand:



$phpapp/consoledebug:container

2.6新版功能:PriortoSymfony2.6,thiscommandwascalledcontainer:debug.



Formoreinformation,seethe服务容器(ServiceContainer)chapter.



生成404或其他错误页面

Whenthingsarenotfound,youshouldplaywellwiththeHTTPprotocolandreturna404response.Todothis,you’llthrowaspecialtypeofexception.Ifyou’reextendingthebasecontrollerclass,dothefollowing:



publicfunctionindexAction()

{

//retrievetheobjectfromdatabase

$product=...;

if(!$product){

throw$this->createNotFoundException(''Theproductdoesnotexist'');

}



return$this->render(...);

}

ThecreateNotFoundException()methodisjustashortcuttocreateaspecialNotFoundHttpExceptionobject,whichultimatelytriggersa404HTTPresponseinsideSymfony.



Ofcourse,you’refreetothrowanyExceptionclassinyourcontroller-Symfonywillautomaticallyreturna500HTTPresponsecode.



thrownew\Exception(''Somethingwentwrong!'');

Ineverycase,anerrorpageisshowntotheenduserandafulldebugerrorpageisshowntothedeveloper(i.e.whenyou’reusingapp_dev.php-see运行环境&FrontControllers).



You’llwanttocustomizetheerrorpageyourusersees.Todothat,seethe“如何自定义错误页面”cookbookrecipe.



Session管理

Symfonyprovidesanicesessionobjectthatyoucanusetostoreinformationabouttheuser(beitarealpersonusingabrowser,abot,orawebservice)betweenrequests.Bydefault,SymfonystorestheattributesinacookiebyusingthenativePHPsessions.



Storingandretrievinginformationfromthesessioncanbeeasilyachievedfromanycontroller:



useSymfony\Component\HttpFoundation\Request;



publicfunctionindexAction(Request$request)

{

$session=$request->getSession();



//storeanattributeforreuseduringalateruserrequest

$session->set(''foo'',''bar'');



//gettheattributesetbyanothercontrollerinanotherrequest

$foobar=$session->get(''foobar'');



//useadefaultvalueiftheattributedoesn''texist

$filters=$session->get(''filters'',array());

}

Theseattributeswillremainontheuserfortheremainderofthatuser’ssession.



Flash消息

Youcanalsostoresmallmessagesthatwillbestoredontheuser’ssessionforexactlyoneadditionalrequest.Thisisusefulwhenprocessingaform:youwanttoredirectandhaveaspecialmessageshownonthenextpage.Thesetypesofmessagesarecalled“flash”messages.



Forexample,imagineyou’reprocessingaformsubmit:



useSymfony\Component\HttpFoundation\Request;



publicfunctionupdateAction(Request$request)

{

$form=$this->createForm(...);



$form->handleRequest($request);



if($form->isValid()){

//dosomesortofprocessing



$this->addFlash(

''notice'',

''Yourchangesweresaved!''

);



//$this->addFlashisequivalentto$this->get(''session'')->getFlashBag()->add



return$this->redirectToRoute(...);

}



return$this->render(...);

}

Afterprocessingtherequest,thecontrollersetsanoticeflashmessageinthesessionandthenredirects.Thename(notice)isn’tsignificant-it’sjustsomethingyouinventandreferencenext.



Inthetemplateofthenextaction,thefollowingcodecouldbeusedtorenderthenoticemessage:



Twig

{%forflashMessageinapp.session.flashbag.get(''notice'')%}



{{flashMessage}}



{%endfor%}

PHP

getFlash(''notice'')as$message):?>



$message"?>





Bydesign,flashmessagesaremeanttoliveforexactlyonerequest(they’re“goneinaflash”).They’redesignedtobeusedacrossredirectsexactlyasyou’vedoneinthisexample.



Response对象

TheonlyrequirementforacontrolleristoreturnaResponseobject.TheResponseclassisanabstractionaroundtheHTTPresponse:thetext-basedmessagefilledwithheadersandcontentthat’ssentbacktotheclient:



useSymfony\Component\HttpFoundation\Response;



//createasimpleResponsewitha200statuscode(thedefault)

$response=newResponse(''Hello''.$name,Response::HTTP_OK);



//createaJSON-responsewitha200statuscode

$response=newResponse(json_encode(array(''name''=>$name)));

$response->www.wang027.comheaders->set(''Content-Type'',''application/json'');

TheheaderspropertyisaHeaderBagobjectandhassomenicemethodsforgettingandsettingtheheaders.TheheadernamesarenormalizedsothatusingContent-Typeisequivalenttocontent-typeorevencontent_type.



Therearealsospecialclassestomakecertainkindsofresponseseasier:



ForJSON,thereisJsonResponse.SeeCreatingaJSONResponse.

Forfiles,thereisBinaryFileResponse.SeeServingFiles.

Forstreamedresponses,thereisStreamedResponse.SeeStreamingaResponse.

参见



Don’tworry!ThereisalotmoreinformationabouttheResponseobjectinthecomponentdocumentation.SeeResponse.

Request对象

Besidesthevaluesoftheroutingplaceholders,thecontrolleralsohasaccesstotheRequestobject.TheframeworkinjectstheRequestobjectinthecontrollerifavariableistype-hintedwithRequest:



useSymfony\Component\HttpFoundation\Request;



publicfunctionindexAction(Request$request)

{

$request->isXmlHttpRequest();//isitanAjaxrequest?



$request->getPreferredLanguage(array(''en'',''fr''));



$request->query->get(''page'');//geta$_GETparameter



$request->request->get(''page'');//geta$_POSTparameter

}

LiketheResponseobject,therequestheadersarestoredinaHeaderBagobjectandareeasilyaccessible.



参见



Don’tworry!ThereisalotmoreinformationabouttheRequestobjectinthecomponentdocumentation.SeeRequest.

创建静态页

Youcancreateastaticpagewithoutevencreatingacontroller(onlyarouteandtemplateareneeded).



SeeHowtoRenderaTemplatewithoutacustomController.



Forward到其他Controller

Thoughnotverycommon,youcanalsoforwardtoanothercontrollerinternallywiththeforward()method.Insteadofredirectingtheuser’sbrowser,itmakesaninternalsub-request,andcallsthecontroller.Theforward()methodreturnstheResponseobjectthat’sreturnedfromthatcontroller:



publicfunctionindexAction($name)

{

$response=$this->forward(''AppBundle:Something:fancy'',array(

''name''=>$name,

''color''=>''green'',

));



//...furthermodifytheresponseorreturnitdirectly



return$response;

}

Noticethattheforward()methodusesaspecialstringrepresentationofthecontroller(seeController命名规则).Inthiscase,thetargetcontrollerfunctionwillbeSomethingController::fancyAction()insidetheAppBundle.Thearraypassedtothemethodbecomestheargumentsontheresultingcontroller.Thissameideaisusedwhenembeddingcontrollersintotemplates(see嵌入Controller).Thetargetcontrollermethodwouldlooksomethinglikethis:



publicfunctionfancyAction($name,$color)

{

//...createandreturnaResponseobject

}

Justlikewhencreatingacontrollerforaroute,theorderoftheargumentsoffancyActiondoesn’tmatter.Symfonymatchestheindexkeynames(e.g.name)withthemethodargumentnames(e.g.$name).Ifyouchangetheorderofthearguments,Symfonywillstillpassthecorrectvaluetoeachvariable.



总结

Wheneveryoucreateapage,you’llultimatelyneedtowritesomecodethatcontainsthelogicforthatpage.InSymfony,thisiscalledacontroller,andit’saPHPfunctionwhereyoucandoanythinginordertoreturnthefinalResponseobjectthatwillbereturnedtotheuser.



Tomakelifeeasier,youcanchoosetoextendabaseControllerclass,whichcontainsshortcutmethodsformanycommoncontrollertasks.Forexample,sinceyoudon’twanttoputHTMLcodeinyourcontroller,youcanusetherender()methodtorenderandreturnthecontentfromatemplate.



Inotherchapters,you’llseehowthecontrollercanbeusedtopersistandfetchobjectsfromadatabase,processformsubmissions,handlecachingandmore.

献花(0)
+1
(本文系thedust79首藏)