控制器(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.
|
|