Share
Embed
Transcript
73 slides
-
the one true framework ?! Symfony is the most popular
PHP framework ?! Symfony is the best PHP framework ?! -
Ex. : Laravel - Components used by this project •
BrowserKit • Console • CssSelector • Debug • DomCrawler • EventDispatcher • Filesystem • Finder • HttpFoundation • HttpKernel • Process • PropertyAccess • Routing • Translation -
History of Symfony • 1.0 - January 2007 • 1.1
- June 2008 • 1.2 - December 2008 • 1.3 - November 2009 • 1.4 - November 2009 • 2.0 - July 2011 • 2.1 - September 2012 • 2.2 - March 2013 • 2.3 - June 2013 • 2.4 - November 2013 • 2.5 - June 2014 -
What is Symfony “ Symfony is a set of PHP
Components, a Web Application framework, a Philosophy, and a Community — all working together in harmony. “ -
Symfony2 • Symfony2 is an open source PHP-based web application
development framework • Based on the Model-View-Controller Design Pattern • It enhances reusability, productivity and maintainability by providing solutions to common web application software problems • The beauty of Symfony2 comes from the fact that it is totally customizable. Use what you need and throw out what you don’t need! -
The Symfony2 Components • HttpFoundation - Contains the Request and
Response classes, as well as other classes for handling sessions and file uploads; • Routing - Powerful and fast routing system that allows you to map a specific URI (e.g. /contact) to some information about how that request should be handled (e.g. execute the contactAction() method); • Form - A full-featured and flexible framework for creating forms and handling form submissions; • Validator - A system for creating rules about data and then validating whether or not user-submitted data follows those rules; • ClassLoader - An autoloading library that allows PHP classes to be used without needing to manually require the files containing those classes; • Templating - A toolkit for rendering templates, handling template inheritance (i.e. a template is decorated with a layout) and performing other common template tasks; • Security - A powerful library for handling all types of security inside an application; • Translation - A framework for translating strings in your application. Each and every one of these components is decoupled and can be used in any PHP project … -
“Create your own framework... on top of the Symfony2 Components”
http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1 -
A Symfony Request in Action # app/config/routing.yml contact: path: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact } // src/Acme/DemoBundle/Controller/MainController.php namespace Acme\DemoBundle\Controller; use Symfony\Component\HttpFoundation\Response; class MainController { public function contactAction() { return new Response('<h1>Contact us!</h1>'); } } -
A Symfony Request in Action # app/config/routing.yml hello: path: /hello/{firstName}/{lastName}
defaults: { _controller: AcmeHelloBundle:Hello:index, color: green } public function indexAction($firstName, $lastName, $color) { // ... } -
HTTP method of a request test_show: path: /test/{var} defaults: {
_controller: DemoBundle:Test:show } methods: [GET] test_update: path: /test/{var} defaults: { _controller: DemoBundle:Test:update } methods: [PUT] test_delete: path: / test/{var} defaults: { _controller: DemoBundle:Test:delete } methods: [DELETE] test_post: path: /test/ defaults: { _controller: DemoBundle:Test:show } methods: [POST] -
Coding Standards • Symfony follows the standards defined in the
PSR-0, PSR-1 and PSR-2 documents. -
Coding Standards - Structure • Add a single space after
each comma delimiter; • Add a single space around operators (==, &&, ...); • Add a comma after each array item in a multi-line array, even after the last one; • Add a blank line before return statements, unless the return is alone inside a statement-group • Declare class properties before methods; • Declare public methods first, then protected ones and finally private ones. -
Coding Standards - Naming Conventions • Use camelCase, not underscores,
for variable, function and method names, arguments; • Prefix abstract classes with Abstract. • Suffix interfaces with Interface; • Suffix traits with Trait; • Suffix exceptions with Exception; -
Coding Standards - Service Naming Conventions • A service name
contains groups, separated by dots • The DI alias of the bundle is the first group (e.g. fos_user); • Use lowercase letters for service and parameter names; • A group name uses the underscore notation -
Directory Structure • app: the application configuration • src: the
project's PHP code • vendor: the third-party dependencies • web: the web root directory -
Bundles “A bundle is a directory that has a well-defined
structure and can host anything from classes to controllers and web resources. “ A bundle is kind of like a plugin in other software… Everything is a bundle in Symfony… A group of files, assets, PHP classes and methods, tests, etc….. - generating a New Bundle Skeleton: php app/console generate:bundle -
Bundles structure 2. MySite\SharedFeatures MySite\Site1Features MySite\Site2Features 1. MySite\MyCode N. or
whatever you want …. . . . . . . -
Bundle directory structure • Bundle system follows set of conventions
to keep code consistent between all bundles Controller/ - contains the controllers of the bundle DependencyInjection/ - (this directory is not necessary) holds certain dependency injection extension classes Resource/config/ - contains configuration, including routing configurations Resource/views/ - holds templates organized by controller name Resource/public/ - contains web assets Tests/ - holds all tests fot the bundle -
Bundle Inheritance to Override parts of a Bundle // src/Acme/UserBundle/AcmeUserBundle.php
namespace Acme\UserBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; class AcmeUserBundle extends Bundle { public function getParent() { return 'FOSUserBundle'; } } -
Some Useful bundles • GearmanBundle - https://github.com/mrazvan92/GearmanBundle • SonataAdminBundle -
https://github.com/sonataproject/SonataAdminBundle • CMFBundle - https://github.com/symfony-cmf/standard-edition • HWIOAuthBundle - https://github.com/hwi/HWIOAuthBundle • MonologBundle - https://github.com/symfony/MonologBundle • SphinxsearchBundle - https://github.com/timewasted/Search- SphinxsearchBundle • FOSRestBundleByExample - https://github.com/sdiaz/FOSRestBundleByExample • MemcacheBundle - https://github.com/tarjei/MemcacheBundle • RedisBundle - https://github.com/da-wen/PhpRedisBundle • SolrBundle - https://github.com/tapronto/SolrBundle -
Service Container A Service Container (or dependency injection container) is
simply a PHP object that manages the instantiation of services (i.e. objects). $ php app/console container:debug - Show all services and the class for each service # app/config/config.yml services: my_mailer: class: Acme\TestBundle\Mailer class TestController extends Controller { public function sendEmailAction() { $mailer = $this->get('my_mailer'); $mailer->send(‘[email protected]', ...); } } -
Create a Console Command class ExampleCommand extends ContainerAwareCommand { protected
function configure() { //name, description, argument } protected function execute(InputInterface $input, OutputInterface $output) { //$this->getContainer() … $name = $input->getArgument('name'); $output->writeln($name); } } Run: php app/console bundle:test Razvan -
Controller • A controller is a PHP function you create
that takes information from the HTTP request and constructs and returns an HTTP response -
Controller - Request & Response • $request->query->get('foo'); // GET •
$request->request->get('bar'); // POST • $request->server->get('HTTP_HOST'); //server variables • $request->getPathInfo(); //gets the URI. -
Controller - Session & Cookie • $session = $this->get('session'); •
$session->get('foo'); • $session->set('foo','bar'); • $request->cookies->get('PHPSESSID'); //cookies -
Controller - Flash messages • $this->get('session')->getFlashBag()->add('notice','message'); • {% for flashMessage
in app.session.flashbag.get('notice') %} <div class="flash notice">{{ flashMessage }}</div> {% endfor %} -
Controller - Simple JSON response in controller use Symfony\Component\HttpFoundation\Response; //
create a simple Response with a 200 status code (the default) $response = new Response('Hello '.$name, Response::HTTP_OK); // create a JSON-response with a 200 status code $response = new Response(json_encode(array('name' => $name))); $response->headers->set('Content-Type', 'application/json'); • $request->isXmlHttpRequest(); // is it an Ajax request? • $response->setStatusCode(500); // creates a Response object whose content is the rended template $response = $this->render(‘DemoBundle:Demo:index.html.twig’); -
Controller - Simple JSON response in controller (JMS) $serializedEntity =
$this->container->get('serializer')->serialize($entity, 'json'); return new Response($serializedEntity); • JMSSerializer - Library for (de-)serializing data of any complexity (supports XML, JSON, YAML) http://jmsyst.com/libs/serializer -
Translations • translations can be created in several different formats:
xliff, php, yml $translated = $this->get('translator')->trans('Symfony2 is great'); In controller In twig {% trans %}Symfony2 is great{% endtrans %} -
Translations echo $translator->transChoice( '[-Inf, 0]There is nothing to delete|{1}Are you
sure you want to delete this file| ]1,10[ %count% files will be deleted|[10,Inf] Are you sure you want to delete all files', $value, array('%count%' => $value) ); -
Validation - The Basics of Validation use Symfony\Component\Validator\Constraints as Assert;
class Entity { /** * @Assert\NotBlank() * @Assert\MaxLenght (100) */ protected $name; } $entity= new Entity(); $validator = $this->get('validator'); $errors = $validator->validate($entity); var_dump($errors); {% for error in errors %} {{ error.message }} {% endfor %} -
Validation - Custom Validation Constraint Creating Constraint class namespace Acme\DemoBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint; class CustomConstraint extends Constraint { public $message = ‘error message'; } Creating the Validator namespace Acme\DemoBundle\Validator\Constraints; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; class CustomValidator extends ConstraintValidator { public function validate($value, Constraint $constraint) { … } } -
Forms - Simple Form use Symfony\Component\Validator\Constraints as Assert; class Demo
{ /** * @Assert\NotBlank() */ protected $name; //setter and getter } -
Forms - Simple Form class DemoType extends AbstractType { public
function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('name'); } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => ‘DemoBundle\Entity\Demo', // 'cascade_validation' => true, 'csrf_protection' => true, )); } public function getName() { return ‘demo'; } } -
Forms - Adding an Event Listener to a Form Class
class DemoType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add(‘name'); $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { $data = $event->getData(); $form = $event->getForm(); // ... $form->add(‘surname’); }); } } -
Forms - Adding an Event Listener to a Form Class
class DemoType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add(‘name'); $builder->addEventSubscriber(new AddNameFieldSubscriber()); } } -
Forms - Adding an Event Listener to a Form Class
class AddNameFieldSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents() { return array(FormEvents::PRE_SET_DATA => 'preSetData'); } public function preSetData(FormEvent $event) { $data = $event->getData(); $form = $event->getForm(); //…. } } -
Security • Security is a two-step process whose goal is
to prevent a user from accessing a resource that they should not have access to. -
Security Check role • $this->get('security.context')->isGranted('ROLE_ADMIN') • $this->get('security.context')->isGranted(new Expression( ‘ "ROLE_ADMIN"
in roles or (user and user.isSuperAdmin()) ' )) Retrieving the User Object • $user = $this->get('security.context')->getToken()->getUser(); • or $this->getUser(); Access Control in Templates • is_granted('ROLE_ADMIN') -
Template • PHP template or Twig • Setup template engine:
yml: templating:engines: ['twig', 'php'] annotation: @Template(engine="php") -
Template - Twig • Twig defines three types of special
syntax: • {{ ... }} - prints a variable • {% ... %} - a tag that controls the logic of the template (if, for etc) • {# ... #} – a comment • Caching {% for entry in blog_entries %} <h2>{{ entry.title }}</h2> <p>{{ entry.body }}</p> {% endfor %} -
Template - Twig - Template Inheritance {# app/Resources/views/base.html.twig #} <!DOCTYPE
html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title> {% block title %}Test Application{% endblock %} </title> </head> <body> <div id="sidebar"> {% block sidebar %} <ul> <li><a href="/">Home</a></li> <li><a href="/blog">Blog</a></li> </ul> {% endblock %} </div> <div id="content"> {% block body %}{% endblock %} </div> </body> </html> -
Template - Twig - Template Inheritance {# src/Acme/BlogBundle/Resources/views/Blog/index.html.twig #} {%
extends '::base.html.twig' %} {% block title %}My cool blog posts{% endblock %} {% block body %} {% for entry in blog_entries %} <h2>{{ entry.title }}</h2> <p>{{ entry.body }}</p> {% endfor %} {% endblock %} -
Template – Twig • {{ parent() }} - add to
the contents of a parent block instead of completely overriding • {{ include() }} - include template in another template • {{ dump(var) }} - debug variables in a template Global variables: • app.security • app.user • app.request • app.request.get('foo') • app.request.request.get('foo') • app.session • app.environment • app.debug -
Template – Twig - Linking to Pages _welcome: path: /
defaults: { _controller: AcmeDemoBundle:Welcome:index } <a href="{{ path('_welcome') }}">Home</a> article_show: path: /article/{slug} defaults: { _controller: AcmeArticleBundle:Article:show } <a href="{{ path('article_show', {'slug': slug}) }}"> Link </a> -
Template – Twig - Linking to Assets <img src="{{ asset('images/logo.png')
}}" alt="Symfony!" /> <link href="{{ asset('css/blog.css') }}" rel="stylesheet" type="text/css" /> -
Twig Extension // src/Acme/DemoBundle/Twig/AcmeExtension.php namespace Acme\DemoBundle\Twig; class AcmeExtension extends \Twig_Extension
{ public function getFilters() { return array(new \Twig_SimpleFilter('price', array($this, 'priceFilter')),); } public function priceFilter($number, $decimals = 0, $decPoint = '.', $thousandsSep = ',') { $price = number_format($number, $decimals, $decPoint, $thousandsSep); $price = '$'.$price; return $price; } public function getName() { return 'acme_extension'; } } -
Twig Extension Register an Extension as a Service: # src/Acme/DemoBundle/Resources/config/services.yml
services: acme.twig.acme_extension: class: Acme\DemoBundle\Twig\AcmeExtension tags:- { name: twig.extension } Using the custom Extension: {{ '5500'|price }} {{ '5500.25155'|price(4, ',', '') }} -
Testing Component • Symfony2 integrates with the PHPUnit testing framework
• All tests must be placed in the Tests/ directory of your project • The test directory will replicate the structure of your bundle’s directory -
Testing Component – Unit test namespace Acme\DemoBundle\Utility; class Calculator {
public function add($a, $b) { return $a + $b; } } The code of the tested controller method: namespace Acme\DemoBundle\Test\Utility; use Acme\DemoBundle\Utility\Calculator; class CalculatorTest extends \PHPUnit_Framework_Test_Case { public function testAdd() { $calc = new Calculator(); $result = $calc->add(30, 12); $this->assertEquals(42, $result); } } The test code: -
Testing Component – Functional test • PHPUnit provides the client
and the crawler objects to help you out • The client simulates a browser by allowing the developer to make requests • The crawler is returned from a client request and allows traversal of the content of the reply • e.g. traverse HTML tags, select nodes, links and forms $buttonCrawlerNode = $crawler->selectButton(‘submit’); $form = $buttonCrawlerNode->form(); $form = $buttomCrawlerNode->form(array( ‘name’ => ‘Symfony’, ‘my_form[subject]’ => ‘Symfony rocks!’, )); $client->submit($form); -
“Hello world!” in Symfony2 • http://localhost/app_dev.php/hello/Arobs • Create the Bundle:
$ php app/console generate:bundle --namespace=Acme/HelloBundle --format=yml // app/AppKernel.php public function registerBundles() { $bundles = array( ..., new Acme\HelloBundle\AcmeHelloBundle(), ); // ... return $bundles; } • Create the Route: # app/config/routing.yml acme_hello: resource: "@AcmeHelloBundle/Resources/config/routing.yml" prefix: / -
“Hello world!” in Symfony2 # src/Acme/HelloBundle/Resources/config/routing.yml hello: path: /hello/{name} defaults:
{ _controller: AcmeHelloBundle:Hello:index } • Create the Controller: // src/Acme/HelloBundle/Controller/HelloController.php namespace Acme\HelloBundle\Controller; use Symfony\Component\HttpFoundation\Response; class HelloController { public function indexAction($name) { return new Response('<html><body>Hello '.$name.'!</body></html>'); } } -
Conclusion • Though Symfony2 is an MVC based framework, it
allows for a great deal of reusability by means of its components • Symfony2 integrates with external components to provide common services. Examples include: • Twig for templating • PHPUnit for testing • Doctrine for database integrating • and many others … • Symfony2 is a full stack web framework