SlideShare ist ein Scribd-Unternehmen logo
1 von 50
Downloaden Sie, um offline zu lesen
Be RESTful

 Fabien Potencier
symfony 1.2 is
REST compliant
 out of the box
symfony 1.2 supports

        GET
       POST
       HEAD
        PUT
      DELETE
To simulate PUT or DELETE
 from a browser, symfony
 uses a special parameter

       sf_method
<form action="#" method="POST">
  <input type="hidden" name="sf_method" value="PUT" />

  <!-- // ... -->
</form>
form_tag('#', array('method' => 'PUT'))



link_to('@article_delete?id=1', array('method' => 'DELETE'))



$b = new sfBrowser();
$b->
  click('/delete', array(), array('method' => 'delete'))->
  // ...
;
$form->renderFormTag('@article_update',
  array('method' => 'PUT')
)



Adds a sf_method hidden field if method is PUT or DELETE

Automatically choose PUT or POST method for a Propel form

Adds the enctype automatically
public function executeUpdate($request)
{
  $this->forward404Unless($request->isMethod('PUT'));

    // or

  throw new LogicException('You must call this page with
 the PUT HTTP method.');

    // ...
}
# apps/frontend/config/routing.yml
article_update:
  url:          /article/:id/update
  param:        { module: article, action: update }
  class:        sfRequestRoute
  requirements: { sf_method: PUT }
# apps/frontend/config/routing.yml
article_update:
  url:          /article/:id
  param:        { module: article, action: update }
  class:        sfRequestRoute
  requirements: { sf_method: PUT }

article_show:
  url:            /article/:id
  param:          { module: article, action: show }
  class:          sfRequestRoute
  requirements:   { sf_method: GET }
Routes are
first-class objects
   in symfony 1.2

      sfRoute
The route object
embarks all the logic


 to match a URL
to generate a URL
The route context

method: PUT, POST, GET, …
format: html, json, css, …
host: The host name
is_secure: https?
request_uri: The requested URI
prefix: The prefix to add to each generated route
Let’s create a new route class

       sfDomainRoute
Some websites have URLs like

http://USERNAME.example.com/...
user_homepage:
  url:     /user
  class:   sfDomainRoute
  params: { module: user, action: index }




public function executeIndex($request)
{
  $username = $request->getAttribute('username');
}
class sfDomainRoute extends sfRequestRoute
{
  public function matchesUrl($url, $context = array())
  {
    if (false === $retval = parent::matchesUrl($url, $context))
    {
      return false;
    }

        $retval[1]['username'] = $this->getSubdomain($context);

        return $retval;
    }

    protected function getSubdomain($context)
    {
      $parts = explode('.', $context['host']);

        return $parts[0];
    }
}
echo url_for('@user_homepage');

// generates /user

echo url_for('@user_homepage?username=foo');

// must generate http://foo.example.com/user
public function matchesParameters($params, $context = array())
{
  unset($params['username']);

    return parent::matchesParameters($params, $context);
}

public function generate($params, $context = array(), $absolute = false)
{
  $subdomain = isset($params['username']) ? $params['username'] : false;
  unset($params['username']);

    if ($subdomain && $subdomain != $this->getSubdomain($context))
    {
      $url = parent::generate($params, $context, false);

        return $this->getHostForSubdomain($context, $subdomain).$url;
    }

    return parent::generate($params, $context, $absolute);
}
protected function getHostForSubdomain($context, $subdomain)
{
   $parts = explode('.', $context['host']);
   $parts[0] = $subdomain;
   $host = implode('.', $parts);
   $protocol = 'http'.(isset($context['is_secure']) &&
  $context['is_secure'] ? 's' : '');

    return $protocol.'://'.$host;
}
$routing = $this->getContext()->getRouting();


echo $routing->generate('user_homepage');



echo $routing->generate('user_homepage', array(), true);



echo $routing->generate('user_homepage', array('username' => 'foo'));
user_homepage:
  url:     /user
  class:   sfObjectDomainRoute
  params: { module: user, action: index }




public function executeIndex($request)
{
  $username = $request->getAttribute('username');
  $user = $request->getAttribute('user');
}
class sfObjectDomainRoute extends sfDomainRoute
{
  public function matchesUrl($url, $context = array())
  {
    if (false === $retval = parent::matchesUrl($url, $context))
    {
      return false;
    }

       $subdomain = $this->getSubdomain($context);
       if (is_null($object = $this->getObject($subdomain)))
       {
         $message = sprintf('Unable to find the "user" object with the following
    subdomain "%s".', $subdomain);
         throw new sfError404Exception($message);
       }

        $retval[1]['user'] = $object;

        return $retval;
    }

    protected function getObject($subdomain)
    {
      return sfGuardUserPeer::retrieveByUsername($subdomain);
    }
}
user_homepage:
  url:     /user
  class:   sfObjectDomainRoute
  params: { module: user, action: index }
  options:
    model: sfGuardUserPeer
    method: retrieveByUsername




public function executeIndex($request)
{
  $username = $request->getAttribute('username');
  $user = $request->getAttribute('user');
}
protected function getObject($subdomain)
{
  if (!isset($this->options['model']))
  {
    throw new InvalidArgumentException('You must pass a "model".');
  }

    if (!isset($this->options['method']))
    {
      throw new InvalidArgumentException('You must pass a "method".');
    }

    return call_user_func(
              array($this->options['model'], $this->options['method']),
             $subdomain
           );
}
A URL is the representation
             of a resource

  A route analyzes a URL to convert it
to parameters that will call a controller

     A route describes a resource
symfony 1.2 introduces
 resources description
in the route definitions
Built-in route classes

      sfRoute
  sfRequestRoute
   sfObjectRoute
   sfPropelRoute
sfPropelRoute
     binds a URL
to a Propel resource
articles:
  url:       /articles
  params:    { module: article, action: list }
  class:     sfPropelRoute
  options:   { model: Article, list: articles }



public function executeList($request)
{
  $this->articles = $request->getAttribute('articles');
}
articles:
  url:     /articles
  params: { module: article, action: list }
  class:    sfPropelRoute
  options:
    model:        Article
    method:       getRecentArticles
    list:         articles
    allow_empty: false
article:
  url:       /article/:id
  params:    { module: article, action: show }
  class:     sfPropelRoute
  options:   { model: Article, object: article }



public function executeShow($request)
{
  $this->article = $request->getAttribute('article');
}
article:
  url:     /article/:slug
  params: { module: article, action: show }
  class:   sfPropelRoute
  options:
    model: Article
    object: article
post:
  url:     /post/:id/:year/:month/:day/:slug
  param:   { module: article, action: show }
  class:   sfPropelRoute
  options: { model: Article, object: article, method:
 getObjectForRoute }

class ArticlePeer extends BaseArticlePeer
{
  static public function getObjectForRoute($parameters)
  {
    $criteria = new Criteria();
    $criteria->add(self::ID, $parameters['id']);

        return self::doSelectOne($criteria);
    }
}
article:
  url:     /article/:id-:slug
  params: { module: article, action: show }
  class:    sfPropelRoute
  options:
    model:               Article
    method:               retrieveOnlineByPk
    object:              article
    allow_empty:          false
    segment_separators: [/, ., -]
url_for('@article?id='.$article->getId().'&slug='.$article
->getSlug()

url_for('article', array('id' => $article->getId(), 'slug' =>
 $article->getSlug()))



url_for('article', $article)


url_for(array('sf_route' => 'article', 'sf_subject' => $article))

url_for('article', array('sf_subject' => $article, 'sf_method' =>
 'get')




link_to('Link to the same page', 'article', $article)
A single entry in routing.yml
can represent a collection of routes

           sfRouteCollection
        sfObjectRouteCollection
        sfPropelRouteCollection
articles:
  class:   sfPropelRouteCollection
  options: { model: Article }
~/work/tmp $ ./symfony app:routes frontend

>> app       Current routes for application "frontend"

Name              Method   Pattern
homepage          GET      /
articles          GET      /articles.:sf_format
articles_new      GET      /articles/new.:sf_format
articles_create   POST     /articles.:sf_format
articles_show     GET      /articles/:id.:sf_format
articles_edit     GET      /articles/:id/edit.:sf_format
articles_update   PUT      /articles/:id.:sf_format
articles_delete   DELETE   /articles/:id.:sf_format
~/work/tmp $ ./symfony app:routes frontend articles_update

>> app                      Route "articles_update" for application "frontend"

Name         articles_update
Pattern      /articles/:id.:sf_format
Class        sfPropelRoute
Defaults     module: 'foo'
             action: 'update'
             sf_format: 'html'
Requirements id: 'd+'
             sf_method: 'put'
             sf_format: '[^/.]+'
Options      suffix: ''
             variable_prefixes: array (0 => ':',)
             segment_separators: array (0 => '/',1 => '.',)
             variable_regex: '[wd_]+'
             generate_shortest_url: true
             extra_parameters_as_query_string: true
             model: 'AdcArticlePeer'
             object: 'article'
             object_model: 'AdcArticle'
             variable_prefix_regex: '(?::)'
             segment_separators_regex: '(?:/|.)'
             variable_content_regex: '[^/.]+'
Regex        #^
             /articles
             /(?P<id>d+)
             (?:.(?P<sf_format>[^/.]+)
             )?
             $#x
Tokens       separator array (0 => '/',)
             text       array (0 => 'articles',)
             separator array (0 => '/',)
             variable   array (0 => ':id',1 => 'id',)
             separator array (0 => '.',)
             variable   array (0 => ':sf_format',1 => 'sf_format',)
./symfony propel:generate-module-for-route frontend articles
class articlesActions extends sfActions
{
  public function executeIndex($request) {}

    public function executeShow($request) {}

    public function executeNew($request) {}

    public function executeCreate($request) {}

    public function executeEdit($request) {}

    public function executeUpdate($request) {}

    public function executeDelete($request) {}

    protected function processForm($request, $form) {}
}
public function executeIndex($request)
{
  $this->articles = $request->getAttribute('articles');
}

public function executeShow($request)
{
  $this->article = $request->getAttribute('article');
}

public function executeNew($request)
{
  $this->form = new AdcArticleForm();
}

public function executeEdit($request)
{
  $this->form = new AdcArticleForm($request->getAttribute('article'));
}

public function executeDelete($request)
{
  $request->getAttribute('article')->delete();

    $this->redirect('@articles');
}
public function executeCreate($request)
{
  $this->form = new AdcArticleForm();

    $this->processForm($request, $this->form);

    $this->setTemplate('new');
}

public function executeUpdate($request)
{
  $this->form = new AdcArticleForm($request->getAttribute('article'));

    $this->processForm($request, $this->form);

    $this->setTemplate('edit');
}

protected function processForm($request, $form)
{
  $form->bind($request->getParameter('adc_article'));
  if ($form->isValid())
  {
    $article = $form->save();

        $this->redirect('@articles_edit?id='.$article->getId());
    }
}
articles:
  class:    sfPropelRouteCollection
  options:
    model:     Article
    plural:    articles
    singular: article
    actions: [list, show, edit, update]
    module:    articles
    column:    slug
    # ...
form_tag_for($form, '@articles')



Automatically choose POST for new objects and PUT for
existing ones

Generates the REST URL thanks to the given route
prefix

Adds the enctype automatically
When will it be available?
… NOW
Questions?
Sensio S.A.
                     92-98, boulevard Victor Hugo
                         92 115 Clichy Cedex
                               FRANCE
                        Tél. : +33 1 40 99 80 80

                                Contact
                           Fabien Potencier
                     fabien.potencier@sensio.com




http://www.sensiolabs.com/                http://www.symfony-project.org/

Weitere ähnliche Inhalte

Was ist angesagt?

Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
Bill Chang
 
sfDay Cologne - Sonata Admin Bundle
sfDay Cologne - Sonata Admin BundlesfDay Cologne - Sonata Admin Bundle
sfDay Cologne - Sonata Admin Bundle
th0masr
 
Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2
Fabien Potencier
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
Fabien Potencier
 
international PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretsinternational PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secrets
smueller_sandsmedia
 
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Fabien Potencier
 

Was ist angesagt? (20)

Doctrine fixtures
Doctrine fixturesDoctrine fixtures
Doctrine fixtures
 
sfDay Cologne - Sonata Admin Bundle
sfDay Cologne - Sonata Admin BundlesfDay Cologne - Sonata Admin Bundle
sfDay Cologne - Sonata Admin Bundle
 
Intro programacion funcional
Intro programacion funcionalIntro programacion funcional
Intro programacion funcional
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
Views notwithstanding
Views notwithstandingViews notwithstanding
Views notwithstanding
 
Decoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDDDecoupling the Ulabox.com monolith. From CRUD to DDD
Decoupling the Ulabox.com monolith. From CRUD to DDD
 
Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2Unit and Functional Testing with Symfony2
Unit and Functional Testing with Symfony2
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
international PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretsinternational PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secrets
 
Fields in Core: How to create a custom field
Fields in Core: How to create a custom fieldFields in Core: How to create a custom field
Fields in Core: How to create a custom field
 
Nashvile Symfony Routes Presentation
Nashvile Symfony Routes PresentationNashvile Symfony Routes Presentation
Nashvile Symfony Routes Presentation
 
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
 
Love and Loss: A Symfony Security Play
Love and Loss: A Symfony Security PlayLove and Loss: A Symfony Security Play
Love and Loss: A Symfony Security Play
 
Functional programming with php7
Functional programming with php7Functional programming with php7
Functional programming with php7
 
Advanced jQuery
Advanced jQueryAdvanced jQuery
Advanced jQuery
 
PHP 8.1: Enums
PHP 8.1: EnumsPHP 8.1: Enums
PHP 8.1: Enums
 
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
Decouple Your Code For Reusability (International PHP Conference / IPC 2008)
 

Andere mochten auch

Andere mochten auch (13)

Building a documented RESTful API in just a few hours with Symfony
Building a documented RESTful API in just a few hours with SymfonyBuilding a documented RESTful API in just a few hours with Symfony
Building a documented RESTful API in just a few hours with Symfony
 
Autenticazione delle api con jwt e symfony (Italian)
Autenticazione delle api con jwt e symfony (Italian)Autenticazione delle api con jwt e symfony (Italian)
Autenticazione delle api con jwt e symfony (Italian)
 
Http and REST APIs.
Http and REST APIs.Http and REST APIs.
Http and REST APIs.
 
Service approach for development REST API in Symfony2
Service approach for development REST API in Symfony2Service approach for development REST API in Symfony2
Service approach for development REST API in Symfony2
 
Service approach for development Rest API in Symfony2
Service approach for development Rest API in Symfony2Service approach for development Rest API in Symfony2
Service approach for development Rest API in Symfony2
 
A high profile project with Symfony and API Platform: beIN SPORTS
A high profile project with Symfony and API Platform: beIN SPORTSA high profile project with Symfony and API Platform: beIN SPORTS
A high profile project with Symfony and API Platform: beIN SPORTS
 
Présentation sur l'accessibilité numérique / Evènement université de Lille 3
Présentation sur l'accessibilité numérique / Evènement université de Lille 3 Présentation sur l'accessibilité numérique / Evènement université de Lille 3
Présentation sur l'accessibilité numérique / Evènement université de Lille 3
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
 
30 Symfony Best Practices
30 Symfony Best Practices30 Symfony Best Practices
30 Symfony Best Practices
 
Symfony in microservice architecture
Symfony in microservice architectureSymfony in microservice architecture
Symfony in microservice architecture
 
Creating hypermedia APIs in a few minutes using the API Platform framework
Creating hypermedia APIs in a few minutes using the API Platform frameworkCreating hypermedia APIs in a few minutes using the API Platform framework
Creating hypermedia APIs in a few minutes using the API Platform framework
 
Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)Symfony: Your Next Microframework (SymfonyCon 2015)
Symfony: Your Next Microframework (SymfonyCon 2015)
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 

Ähnlich wie Be RESTful (Symfony Camp 2008)

Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)
Luka Zakrajšek
 
Aura Project for PHP
Aura Project for PHPAura Project for PHP
Aura Project for PHP
Hari K T
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
Jeremy Kendall
 

Ähnlich wie Be RESTful (Symfony Camp 2008) (20)

Advanced symfony Techniques
Advanced symfony TechniquesAdvanced symfony Techniques
Advanced symfony Techniques
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
laravel tricks in 50minutes
laravel tricks in 50minuteslaravel tricks in 50minutes
laravel tricks in 50minutes
 
50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes50 Laravel Tricks in 50 Minutes
50 Laravel Tricks in 50 Minutes
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
WordPress REST API hacking
WordPress REST API hackingWordPress REST API hacking
WordPress REST API hacking
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Easy rest service using PHP reflection api
Easy rest service using PHP reflection apiEasy rest service using PHP reflection api
Easy rest service using PHP reflection api
 
Apostrophe
ApostropheApostrophe
Apostrophe
 
Mashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web AppsMashing up JavaScript – Advanced Techniques for modern Web Apps
Mashing up JavaScript – Advanced Techniques for modern Web Apps
 
Django Class-based views (Slovenian)
Django Class-based views (Slovenian)Django Class-based views (Slovenian)
Django Class-based views (Slovenian)
 
Aura Project for PHP
Aura Project for PHPAura Project for PHP
Aura Project for PHP
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Mashing up JavaScript
Mashing up JavaScriptMashing up JavaScript
Mashing up JavaScript
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
Lithium Best
Lithium Best Lithium Best
Lithium Best
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
 
Bacbkone js
Bacbkone jsBacbkone js
Bacbkone js
 

Mehr von Fabien Potencier (20)

Varnish
VarnishVarnish
Varnish
 
Look beyond PHP
Look beyond PHPLook beyond PHP
Look beyond PHP
 
Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4Dependency injection in PHP 5.3/5.4
Dependency injection in PHP 5.3/5.4
 
Dependency injection-zendcon-2010
Dependency injection-zendcon-2010Dependency injection-zendcon-2010
Dependency injection-zendcon-2010
 
Caching on the Edge
Caching on the EdgeCaching on the Edge
Caching on the Edge
 
Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3Design patterns revisited with PHP 5.3
Design patterns revisited with PHP 5.3
 
PhpBB meets Symfony2
PhpBB meets Symfony2PhpBB meets Symfony2
PhpBB meets Symfony2
 
Dependency injection - phpday 2010
Dependency injection - phpday 2010Dependency injection - phpday 2010
Dependency injection - phpday 2010
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 
Symfony2 - OSIDays 2010
Symfony2 - OSIDays 2010Symfony2 - OSIDays 2010
Symfony2 - OSIDays 2010
 
Dependency Injection IPC 201
Dependency Injection IPC 201Dependency Injection IPC 201
Dependency Injection IPC 201
 
Caching on the Edge with Symfony2
Caching on the Edge with Symfony2Caching on the Edge with Symfony2
Caching on the Edge with Symfony2
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
 
Dependency Injection - ConFoo 2010
Dependency Injection - ConFoo 2010Dependency Injection - ConFoo 2010
Dependency Injection - ConFoo 2010
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
Symfony Components
Symfony ComponentsSymfony Components
Symfony Components
 
PHP 5.3 in practice
PHP 5.3 in practicePHP 5.3 in practice
PHP 5.3 in practice
 
Symfony2 revealed
Symfony2 revealedSymfony2 revealed
Symfony2 revealed
 
Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3
 

Kürzlich hochgeladen

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 

Kürzlich hochgeladen (20)

Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 

Be RESTful (Symfony Camp 2008)

  • 1. Be RESTful Fabien Potencier
  • 2. symfony 1.2 is REST compliant out of the box
  • 3. symfony 1.2 supports GET POST HEAD PUT DELETE
  • 4. To simulate PUT or DELETE from a browser, symfony uses a special parameter sf_method
  • 5. <form action="#" method="POST"> <input type="hidden" name="sf_method" value="PUT" /> <!-- // ... --> </form>
  • 6. form_tag('#', array('method' => 'PUT')) link_to('@article_delete?id=1', array('method' => 'DELETE')) $b = new sfBrowser(); $b-> click('/delete', array(), array('method' => 'delete'))-> // ... ;
  • 7. $form->renderFormTag('@article_update', array('method' => 'PUT') ) Adds a sf_method hidden field if method is PUT or DELETE Automatically choose PUT or POST method for a Propel form Adds the enctype automatically
  • 8. public function executeUpdate($request) { $this->forward404Unless($request->isMethod('PUT')); // or throw new LogicException('You must call this page with the PUT HTTP method.'); // ... }
  • 9. # apps/frontend/config/routing.yml article_update: url: /article/:id/update param: { module: article, action: update } class: sfRequestRoute requirements: { sf_method: PUT }
  • 10. # apps/frontend/config/routing.yml article_update: url: /article/:id param: { module: article, action: update } class: sfRequestRoute requirements: { sf_method: PUT } article_show: url: /article/:id param: { module: article, action: show } class: sfRequestRoute requirements: { sf_method: GET }
  • 11. Routes are first-class objects in symfony 1.2 sfRoute
  • 12. The route object embarks all the logic to match a URL to generate a URL
  • 13. The route context method: PUT, POST, GET, … format: html, json, css, … host: The host name is_secure: https? request_uri: The requested URI prefix: The prefix to add to each generated route
  • 14. Let’s create a new route class sfDomainRoute
  • 15. Some websites have URLs like http://USERNAME.example.com/...
  • 16. user_homepage: url: /user class: sfDomainRoute params: { module: user, action: index } public function executeIndex($request) { $username = $request->getAttribute('username'); }
  • 17. class sfDomainRoute extends sfRequestRoute { public function matchesUrl($url, $context = array()) { if (false === $retval = parent::matchesUrl($url, $context)) { return false; } $retval[1]['username'] = $this->getSubdomain($context); return $retval; } protected function getSubdomain($context) { $parts = explode('.', $context['host']); return $parts[0]; } }
  • 18. echo url_for('@user_homepage'); // generates /user echo url_for('@user_homepage?username=foo'); // must generate http://foo.example.com/user
  • 19. public function matchesParameters($params, $context = array()) { unset($params['username']); return parent::matchesParameters($params, $context); } public function generate($params, $context = array(), $absolute = false) { $subdomain = isset($params['username']) ? $params['username'] : false; unset($params['username']); if ($subdomain && $subdomain != $this->getSubdomain($context)) { $url = parent::generate($params, $context, false); return $this->getHostForSubdomain($context, $subdomain).$url; } return parent::generate($params, $context, $absolute); }
  • 20. protected function getHostForSubdomain($context, $subdomain) { $parts = explode('.', $context['host']); $parts[0] = $subdomain; $host = implode('.', $parts); $protocol = 'http'.(isset($context['is_secure']) && $context['is_secure'] ? 's' : ''); return $protocol.'://'.$host; }
  • 21. $routing = $this->getContext()->getRouting(); echo $routing->generate('user_homepage'); echo $routing->generate('user_homepage', array(), true); echo $routing->generate('user_homepage', array('username' => 'foo'));
  • 22. user_homepage: url: /user class: sfObjectDomainRoute params: { module: user, action: index } public function executeIndex($request) { $username = $request->getAttribute('username'); $user = $request->getAttribute('user'); }
  • 23. class sfObjectDomainRoute extends sfDomainRoute { public function matchesUrl($url, $context = array()) { if (false === $retval = parent::matchesUrl($url, $context)) { return false; } $subdomain = $this->getSubdomain($context); if (is_null($object = $this->getObject($subdomain))) { $message = sprintf('Unable to find the "user" object with the following subdomain "%s".', $subdomain); throw new sfError404Exception($message); } $retval[1]['user'] = $object; return $retval; } protected function getObject($subdomain) { return sfGuardUserPeer::retrieveByUsername($subdomain); } }
  • 24. user_homepage: url: /user class: sfObjectDomainRoute params: { module: user, action: index } options: model: sfGuardUserPeer method: retrieveByUsername public function executeIndex($request) { $username = $request->getAttribute('username'); $user = $request->getAttribute('user'); }
  • 25. protected function getObject($subdomain) { if (!isset($this->options['model'])) { throw new InvalidArgumentException('You must pass a "model".'); } if (!isset($this->options['method'])) { throw new InvalidArgumentException('You must pass a "method".'); } return call_user_func( array($this->options['model'], $this->options['method']), $subdomain ); }
  • 26. A URL is the representation of a resource A route analyzes a URL to convert it to parameters that will call a controller A route describes a resource
  • 27. symfony 1.2 introduces resources description in the route definitions
  • 28. Built-in route classes sfRoute sfRequestRoute sfObjectRoute sfPropelRoute
  • 29. sfPropelRoute binds a URL to a Propel resource
  • 30. articles: url: /articles params: { module: article, action: list } class: sfPropelRoute options: { model: Article, list: articles } public function executeList($request) { $this->articles = $request->getAttribute('articles'); }
  • 31. articles: url: /articles params: { module: article, action: list } class: sfPropelRoute options: model: Article method: getRecentArticles list: articles allow_empty: false
  • 32. article: url: /article/:id params: { module: article, action: show } class: sfPropelRoute options: { model: Article, object: article } public function executeShow($request) { $this->article = $request->getAttribute('article'); }
  • 33. article: url: /article/:slug params: { module: article, action: show } class: sfPropelRoute options: model: Article object: article
  • 34. post: url: /post/:id/:year/:month/:day/:slug param: { module: article, action: show } class: sfPropelRoute options: { model: Article, object: article, method: getObjectForRoute } class ArticlePeer extends BaseArticlePeer { static public function getObjectForRoute($parameters) { $criteria = new Criteria(); $criteria->add(self::ID, $parameters['id']); return self::doSelectOne($criteria); } }
  • 35. article: url: /article/:id-:slug params: { module: article, action: show } class: sfPropelRoute options: model: Article method: retrieveOnlineByPk object: article allow_empty: false segment_separators: [/, ., -]
  • 36. url_for('@article?id='.$article->getId().'&slug='.$article ->getSlug() url_for('article', array('id' => $article->getId(), 'slug' => $article->getSlug())) url_for('article', $article) url_for(array('sf_route' => 'article', 'sf_subject' => $article)) url_for('article', array('sf_subject' => $article, 'sf_method' => 'get') link_to('Link to the same page', 'article', $article)
  • 37. A single entry in routing.yml can represent a collection of routes sfRouteCollection sfObjectRouteCollection sfPropelRouteCollection
  • 38. articles: class: sfPropelRouteCollection options: { model: Article }
  • 39. ~/work/tmp $ ./symfony app:routes frontend >> app Current routes for application "frontend" Name Method Pattern homepage GET / articles GET /articles.:sf_format articles_new GET /articles/new.:sf_format articles_create POST /articles.:sf_format articles_show GET /articles/:id.:sf_format articles_edit GET /articles/:id/edit.:sf_format articles_update PUT /articles/:id.:sf_format articles_delete DELETE /articles/:id.:sf_format
  • 40. ~/work/tmp $ ./symfony app:routes frontend articles_update >> app Route "articles_update" for application "frontend" Name articles_update Pattern /articles/:id.:sf_format Class sfPropelRoute Defaults module: 'foo' action: 'update' sf_format: 'html' Requirements id: 'd+' sf_method: 'put' sf_format: '[^/.]+' Options suffix: '' variable_prefixes: array (0 => ':',) segment_separators: array (0 => '/',1 => '.',) variable_regex: '[wd_]+' generate_shortest_url: true extra_parameters_as_query_string: true model: 'AdcArticlePeer' object: 'article' object_model: 'AdcArticle' variable_prefix_regex: '(?::)' segment_separators_regex: '(?:/|.)' variable_content_regex: '[^/.]+' Regex #^ /articles /(?P<id>d+) (?:.(?P<sf_format>[^/.]+) )? $#x Tokens separator array (0 => '/',) text array (0 => 'articles',) separator array (0 => '/',) variable array (0 => ':id',1 => 'id',) separator array (0 => '.',) variable array (0 => ':sf_format',1 => 'sf_format',)
  • 42. class articlesActions extends sfActions { public function executeIndex($request) {} public function executeShow($request) {} public function executeNew($request) {} public function executeCreate($request) {} public function executeEdit($request) {} public function executeUpdate($request) {} public function executeDelete($request) {} protected function processForm($request, $form) {} }
  • 43. public function executeIndex($request) { $this->articles = $request->getAttribute('articles'); } public function executeShow($request) { $this->article = $request->getAttribute('article'); } public function executeNew($request) { $this->form = new AdcArticleForm(); } public function executeEdit($request) { $this->form = new AdcArticleForm($request->getAttribute('article')); } public function executeDelete($request) { $request->getAttribute('article')->delete(); $this->redirect('@articles'); }
  • 44. public function executeCreate($request) { $this->form = new AdcArticleForm(); $this->processForm($request, $this->form); $this->setTemplate('new'); } public function executeUpdate($request) { $this->form = new AdcArticleForm($request->getAttribute('article')); $this->processForm($request, $this->form); $this->setTemplate('edit'); } protected function processForm($request, $form) { $form->bind($request->getParameter('adc_article')); if ($form->isValid()) { $article = $form->save(); $this->redirect('@articles_edit?id='.$article->getId()); } }
  • 45. articles: class: sfPropelRouteCollection options: model: Article plural: articles singular: article actions: [list, show, edit, update] module: articles column: slug # ...
  • 46. form_tag_for($form, '@articles') Automatically choose POST for new objects and PUT for existing ones Generates the REST URL thanks to the given route prefix Adds the enctype automatically
  • 47. When will it be available?
  • 50. Sensio S.A. 92-98, boulevard Victor Hugo 92 115 Clichy Cedex FRANCE Tél. : +33 1 40 99 80 80 Contact Fabien Potencier fabien.potencier@sensio.com http://www.sensiolabs.com/ http://www.symfony-project.org/