<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Amicalement Web - Astuces et Bons plans dans le développement web &#187; Tutorials</title>
	<atom:link href="http://www.amicalement-web.net/tag/tutorial/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.amicalement-web.net</link>
	<description>Astuces et bons plans d&#039;un web developpeur</description>
	<lastBuildDate>Wed, 04 Jan 2012 14:54:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Symfony2 : créer un admin sécurisé</title>
		<link>http://www.amicalement-web.net/symfony2-creer-un-admin-securise/2010/12/23/</link>
		<comments>http://www.amicalement-web.net/symfony2-creer-un-admin-securise/2010/12/23/#comments</comments>
		<pubDate>Thu, 23 Dec 2010 10:00:25 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[admin]]></category>
		<category><![CDATA[config]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[symfony2]]></category>

		<guid isPermaLink="false">http://www.amicalement-web.net/?p=1603</guid>
		<description><![CDATA[Non je ne suis pas mort ! Une grosse année qui arrive à son terme et qui m&#8217;aura un peu éloigné du blog ces derniers mois, mais me voilà de retour aux affaires avec la sortie imminente de Symfony2. J&#8217;ai donc décidé de monter un petit site pour tester en conditions réelles ce nouveau framework. [...]]]></description>
			<content:encoded><![CDATA[<p><a  href="http://www.amicalement-web.net/symfony2-creer-un-admin-securise/2010/12/22/"><img src="http://www.amicalement-web.net/wp-content/uploads/Sans-titre-3.png" alt="" title="Sans-titre-3" width="614" height="100" class="alignnone size-full wp-image-1617" /></a><br />
Non je ne suis pas mort ! Une grosse année qui arrive à son terme et qui m&#8217;aura un peu éloigné du blog ces derniers mois, mais me voilà de retour aux affaires avec la sortie imminente de Symfony2. J&#8217;ai donc décidé de monter un petit site pour tester en conditions réelles ce nouveau framework. Et tant qu&#8217;à débroussailler le terrain, autant faire partager mes difficultés.</p>
<p>L&#8217;idée dans le tuto d&#8217;aujourd&#8217;hui est de faire un petit point sur la partie &laquo;&nbsp;security&nbsp;&raquo; très bien documentée sur le site officiel mais qui change assez des habitudes de symfony 1 et donc semble un peu déroutante au début.<br />
<span id="more-1603"></span></p>
<blockquote><p>
Ce billet est issu de ma propre et récente expérience sur le sujet et sur la branche master de fabien. Les choses peuvent changer ou être inexactes. Dans ce dernier cas, n&#8217;hésitez pas à me reprendre via les commentaires ;)
</p></blockquote>
<h3>Contexte</h3>
<p>Pour l&#8217;illustration, je vais simplement partir de la sandbox officielle d&#8217;origine configurée sur l&#8217;url suivante en local chez moi :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">http:<span style="color: #000000; font-weight: bold;">//</span>sandbox.local</pre></div></div>

<p> et où je vais vous montrer comment y intégrer votre admin avec un formulaire d&#8217;identification sur l&#8217;url suivante :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">http:<span style="color: #000000; font-weight: bold;">//</span>sandbox.local<span style="color: #000000; font-weight: bold;">/</span>admin<span style="color: #000000; font-weight: bold;">/</span></pre></div></div>

<p>L&#8217;utilisateur sera géré directement via la config, pas de bdd, on veut quelque chose de très simple et rapide à mettre en place.</p>
<p>Première chose à comprendre dans une application Symfony2, il n&#8217;y a plus qu&#8217;une application&#8230; Oui hein, ça parait fou dis comme ça :D Toute la séparation se situe au niveau des bundles, chacun d&#8217;eux pouvant embarquer des modèles, controllers, vues différentes, tout en pouvant communiquer avec les autres bundles. Fini les galères pour faire des liens entre un backend et un frontend !</p>
<p>Pour notre admin, nous allons donc générer un nouveau bundle dans notre application via la commande :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">php app<span style="color: #000000; font-weight: bold;">/</span>console init:bundle Application<span style="color: #000000; font-weight: bold;">/</span>AdminBundle</pre></div></div>

<p>On se retrouve donc avec un controller basique dans ce bundle qui sera la base de notre admin. Première chose à faire, l&#8217;activer dans le fichier <code>app/AppKernel.php</code> via la méthode <code>registerBundles</code>.</p>
<h3>Mise en place url admin</h3>
<p>On va maintenant tenter d&#8217;y accéder via l&#8217;url <code>/admin/</code></p>
<p>Pour ça, c&#8217;est très simple ! On va modifier notre fichier routing.yml (ou xml ou php hein, mais moi j&#8217;aime bien le yml ! ) comme ceci :</p>

<div class="wp_syntax"><div class="code"><pre class="json" style="font-family:monospace;">#path:app/config/routing.yml
&nbsp;
homepage:
    pattern:  /
    defaults: { _controller: FrameworkBundle:Default:index }
&nbsp;
hello:
    resource: HelloBundle/Resources/config/routing.yml
&nbsp;
admin:
   resource: AdminBundle/Resources/config/routing.yml
   prefix: /admin</pre></div></div>

<p>et du coup créer le fichier de routing de notre nouveau AdminBundle. Pour ça, on va faire quelque chose de très simple aussi:</p>

<div class="wp_syntax"><div class="code"><pre class="yaml" style="font-family:monospace;">#src/Application/AdminBundle/Resources/config/routing.yml
admin_homepage:
  pattern:  /
  defaults: { _controller: AdminBundle:Default:index }</pre></div></div>

<p>Et par magie, maintenant, si vous accédez à l&#8217;url :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">http:<span style="color: #000000; font-weight: bold;">//</span>sandbox.local<span style="color: #000000; font-weight: bold;">/</span>admin<span style="color: #000000; font-weight: bold;">/</span></pre></div></div>

<p>Vous obtenez le mot &laquo;&nbsp;Hello&nbsp;&raquo; qui n&#8217;est d&#8217;autre que la vue par défaut qui a été générée par la commande <code>init:bundle</code><br />
Pour pas se perdre, on va modifier la vue, pour y indiquer le mot admin, ca sera plus clair.</p>
<h3>Sécuriser l&#8217;url d&#8217;admin</h3>
<p>2e étape, comment sécuriser tout ça pour ne pas laisser tout le monde accéder à votre admin. Pour ça il y a en fait plusieurs solutions.</p>
<ul>
<li>Sécuriser toutes les urls de votre application, en autorisant les connexions anonymes partout, sauf dans votre admin</li>
<li>Sécuriser toutes les urls de votre admin seulement</li>
</ul>
<p>La 1ere a l&#8217;avantage de faire écrire très peu de ligne de config, mais honnêtement, je ne sais pas s&#8217;il y en a une de meilleure que l&#8217;autre, donc je présente la première dans ce tuto, mais je vous met le code de la deuxième à la fin. Elles font sensiblement la même chose.</p>
<p>On va donc se lancer dans la config du component security. Pour ça, on se retrouve dans notre <code>config.yml</code> pour y ajouter un provider, qui sera en fait le compte utilisateur de notre admin.</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;">#app/config/config.yml
security.config:
  providers:
    admin:
      users:
        monadmin: { password: monpass, roles: ROLE_ADMIN }</pre></div></div>

<p>Nous voici avec un compte &laquo;&nbsp;monadmin&nbsp;&raquo; identifié grâce au mot de passe &laquo;&nbsp;monpass&nbsp;&raquo; qui sera crédité de l&#8217;autorisation &laquo;&nbsp;ROLE_ADMIN&nbsp;&raquo; (pour pourriez mettre TOTO à la place de ADMIN, ca changerait rien, par contrel mot ROLE est apparemment obligatoire) </p>
<p>Pour l&#8217;instant, ca ne change rien. Définissons maintenant une règle firewall ! Le mot utilisé pour définir ce nouveau service fait sourire j&#8217;avoue, en fait il s&#8217;agit de définir des règles de sécurité pour des motifs d&#8217;urls.</p>
<p>On reste dans notre config.yml qu&#8217;on enrichit comme ceci:</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;">#app/config/config.yml
security.config:
  providers:
    main:
      users:
        monadmin: { password: monpass, roles: ROLE_ADMIN  }
&nbsp;
  firewalls:
    admin:
      pattern: .*
      http_basic: true</pre></div></div>

<p>Et là, vous obtenez normalement une demande d&#8217;authentification HTTP sur tout votre site. Ok! Maintenant, on va autoriser des connexions anonymes, c&#8217;est à dire qu&#8217;on ne va pas forcer les gens à se connecter:</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;">#app/config/config.yml
security.config:
  providers:
    main:
      users:
        monadmin: { password: monpass, roles: ROLE_ADMIN }
&nbsp;
  firewalls:
    admin:
      pattern: .*
      http_basic: true
      anonymous: true</pre></div></div>

<p>Maintenant, voyons voir pour mettre en place un formulaire plus conventionnel (et il me semble plus secure si bien fait, mais à confirmer).</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;">#app/config/config.yml
security.config:
  providers:
    main:
      users:
        monadmin: { password: monpass, roles: ROLE_ADMIN }
&nbsp;
  firewalls:
    admin:
      pattern: .*
      form_login: true
      anonymous: true</pre></div></div>

<p>Ici on a changé le mode d&#8217;authentification pour celui d&#8217;un formulaire classique, grâce à l&#8217;option <code>form-login</code></p>
<p>Maintenant, on va verrouiller l&#8217;accès à notre admin. Pour ce faire, on va demander une autorisation pour notre url <code>/admin</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;">#app/config/config.yml
security.config:
  providers:
    main:
      users:
        monadmin: { password: monpass, roles: ROLE_ADMIN }
&nbsp;
  firewalls:
    admin:
      pattern: .*
      form_login: true
      anonymous: true
&nbsp;
  access_control:
      - { path: /admin/.*, role: ROLE_ADMIN }</pre></div></div>

<p>Et maintenant, si vous essayez d&#8217;accéder à l&#8217;url :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">http:<span style="color: #000000; font-weight: bold;">//</span>sandbox.local<span style="color: #000000; font-weight: bold;">/</span>admin<span style="color: #000000; font-weight: bold;">/</span></pre></div></div>

<p>Vous êtes redirigés vers l&#8217;url:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">http:<span style="color: #000000; font-weight: bold;">//</span>sandbox.local<span style="color: #000000; font-weight: bold;">/</span>login<span style="color: #000000; font-weight: bold;">/</span></pre></div></div>

<p>qui se termine en 404! Jusqu&#8217;ici c&#8217;est normal.</p>
<p>Symfony2 par défaut, utilise 3 urls pour la gestion de l&#8217;authentification :</p>
<ul>
<li>login</li>
<li>login_check</li>
<li>logout</li>
</ul>
<p>Nous allons donc les rajouter dans notre routing.yml tout simplement. Attention de bien les mettre au début, histoire d&#8217;éviter tout conflit avec une autre de vos règles.</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;">_security_login:
  pattern: /login
  defaults: { _controller: AdminBundle:Default:login }
&nbsp;
_security_check:
  pattern: /login_check
&nbsp;
_security_logout:
    pattern: /logout</pre></div></div>

<p>Le login a besoin de nous pour constituer le formulaire d&#8217;identification, les 2 autres doivent seulement exister ! Les patterns des urls peuvent varier, mais il faut alors donner les nouveaux paths dans le config.yml, nous verrons ça plus tard.</p>
<p>Maintenant, si nous accédons à notre admin nous avons bien sûr l&#8217;exception suivante:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">Method <span style="color: #0000ff;">&quot;Application\AdminBundle\Controller\DefaultController::loginAction&quot;</span> does not exist<span style="color: #339933;">.</span></pre></div></div>

<p>On la crée donc:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #666666; font-style: italic;">// src/Application/AdminBundle/Controller/DefaultController.php</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">namespace</span> Application\AdminBundle\Controller<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">use</span> Symfony\Bundle\FrameworkBundle\Controller\Controller<span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">use</span> Symfony\Component\Security\SecurityContext<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// à rajouter</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">class</span> DefaultController <span style="color: #000000; font-weight: bold;">extends</span> Controller
<span style="color: #009900;">&#123;</span>
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> indexAction<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">render</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'AdminBundle:Default:index.php'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> loginAction<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
       <span style="color: #666666; font-style: italic;">// get the error if any (works with forward and redirect -- see below)</span>
      <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'request'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">attributes</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">has</span><span style="color: #009900;">&#40;</span>SecurityContext<span style="color: #339933;">::</span><span style="color: #004000;">AUTHENTICATION_ERROR</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
          <span style="color: #000088;">$error</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'request'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">attributes</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get</span><span style="color: #009900;">&#40;</span>SecurityContext<span style="color: #339933;">::</span><span style="color: #004000;">AUTHENTICATION_ERROR</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
          <span style="color: #000088;">$error</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'request'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getSession</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get</span><span style="color: #009900;">&#40;</span>SecurityContext<span style="color: #339933;">::</span><span style="color: #004000;">AUTHENTICATION_ERROR</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
&nbsp;
      <span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">render</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'AdminBundle:Default:login.twig'</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
          <span style="color: #666666; font-style: italic;">// last username entered by the user</span>
          <span style="color: #0000ff;">'last_username'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'request'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getSession</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">get</span><span style="color: #009900;">&#40;</span>SecurityContext<span style="color: #339933;">::</span><span style="color: #004000;">LAST_USERNAME</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
          <span style="color: #0000ff;">'error'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$error</span><span style="color: #339933;">,</span>
      <span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Donc là, j&#8217;ai simplement récupéré le code fourni dans la doc officielle, pas de mystère non plus. Et voici la vue correspondante :</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;form</span> <span style="color: #000066;">action</span>=<span style="color: #ff0000;">&quot;{% path &quot;</span>_security_check<span style="color: #ff0000;">&quot; %}&quot;</span> <span style="color: #000066;">method</span>=<span style="color: #ff0000;">&quot;post&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
&nbsp;
  {% if error %}
	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;div</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;notification error png_bg&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
		<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;div<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
			{{ error }}
		<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/div<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/div<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
  {% endif %}
&nbsp;
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;p<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
      <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;label<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Username<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/label<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;input</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;text-input&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;text&quot;</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;_username&quot;</span> <span style="color: #000066;">value</span>=<span style="color: #ff0000;">&quot;{{ last_username }}&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
   <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/p<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
   <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;p<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
      <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;label<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Password<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/label<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
      <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;input</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;text-input&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;password&quot;</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;_password&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
   <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/p<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
   <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;p</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">&quot;remember-password&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
      <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;input</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;checkbox&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>Remember me
   <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/p<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
&nbsp;
   <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;p<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
	<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;input</span> <span style="color: #000066;">class</span>=<span style="color: #ff0000;">&quot;button&quot;</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">&quot;submit&quot;</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;login&quot;</span> <span style="color: #000066;">value</span>=<span style="color: #ff0000;">&quot;Sign In&quot;</span> <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
   <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/p<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/form<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>Là aussi, très basique mais ça vous donne l&#8217;occasion d&#8217;essayer twig ;) Ce qui compte pour le coup, c&#8217;est surtout les attributs name des input (_username et _password), et de rediriger vers <code>/login_check</code></p>
<blockquote><p>
A noter que l&#8217;utilisation du tag path dans la dernière version de twig n&#8217;est plus d&#8217;actualité. C&#8217;est désormais une fonction, donc à utiliser comme ceci {{ path(&#8216;_security_check&#8217;) }}
</p></blockquote>
<p>Alors oui, il faudrait un layout et tout, mais bon, on va aller droit à l&#8217;essentiel.</p>
<p>On retrouve bien notre formulaire de login maintenant, si on essaye d&#8217;accéder à notre admin ! Encore mieux, si on essaye de s&#8217;authentifier, avec le couple login/password défini au début, on obtient notre page &laquo;&nbsp;admin!&nbsp;&raquo; Et il suffit d&#8217;ajouter une dernière ligne à notre config, pour ajouter le listener manquant sur le <code>/logout</code> :</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;">#app/config/config.yml
security.config:
  providers:
    main:
      users:
        monadmin: { password: monpass, roles: ROLE_ADMIN }
&nbsp;
  firewalls:
    admin:
      pattern: .*
      form_login: true
      anonymous: true
      logout: true
&nbsp;
  access_control:
      - { path: /admin/.*, role: ROLE_ADMIN }</pre></div></div>

<p>Et maintenant, vous pouvez vous déloguer sur l&#8217;url :</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">http:<span style="color: #000000; font-weight: bold;">//</span>sandbox.local<span style="color: #000000; font-weight: bold;">/</span><span style="color: #7a0874; font-weight: bold;">logout</span></pre></div></div>

<p>Histoire d&#8217;illustrer un peu plus, voici une autre config qui fonctionne aussi et qui permet de ne sécuriser que l&#8217;admin:</p>

<div class="wp_syntax"><div class="code"><pre class="yml" style="font-family:monospace;">security.config:
  providers:
    main:
      users:
        monadmin: { password: monpass, roles: ROLE_TOTO }
&nbsp;
  firewalls:
    admin: { pattern: /admin/.*, form_login: true }
    login: { pattern: /login, anonymous: true, form_login: true }
    login_check: { pattern: /login_check, anonymous: true, form_login: true }
    logout: { pattern: /logout, form_login: true, logout: true }
&nbsp;
  access_control:
       - { path: /admin/.*, role: ROLE_TOTO }</pre></div></div>

<p>Il y a bien sûr, beaucoup de tuning possible, pour encoder le password par exemple, mais maintenant qu&#8217;on a la base, je vous laisse <a  href="http://docs.symfony-reloaded.org/guides/security/index.html" rel="extern">lire vraiment la doc</a> entière cette fois-ci ;)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.amicalement-web.net/symfony2-creer-un-admin-securise/2010/12/23/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Symfony, les forms et les forms embarqués</title>
		<link>http://www.amicalement-web.net/symfony-les-forms-et-les-forms-embarques/2010/03/08/</link>
		<comments>http://www.amicalement-web.net/symfony-les-forms-et-les-forms-embarques/2010/03/08/#comments</comments>
		<pubDate>Mon, 08 Mar 2010 10:00:00 +0000</pubDate>
		<dc:creator>Vince</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[conception]]></category>
		<category><![CDATA[embed]]></category>
		<category><![CDATA[form]]></category>
		<category><![CDATA[poo]]></category>
		<category><![CDATA[Symfony]]></category>

		<guid isPermaLink="false">http://www.amicalement-web.net/?p=896</guid>
		<description><![CDATA[Tout d&#8217;abord admirez, dans le titre, cette traduction d&#8217;embedded forms , qui est digne d&#8217;un Nelson (ou d&#8217;un ubermuda) en pleine forme. Maintenant que c&#8217;est fait, nous allons parler ici d&#8217;un des aspects de symfony les plus intéressant, mais aussi d&#8217;un des moins bien documenté. Pas que les forms &#171;&#160;basiques&#160;&#187; ne soient pas documentés, mais [...]]]></description>
			<content:encoded><![CDATA[<p><a  href="http://www.amicalement-web.net/symfony-les-forms-et-les-forms-embarques/2010/03/08/"><img src="http://www.amicalement-web.net/wp-content/uploads/boats.jpg" alt="" title="boats" width="614" height="100" class="alignnone size-full wp-image-1368" /></a> Tout d&#8217;abord admirez, dans le titre, cette traduction d&#8217;<em>embedded forms</em> , qui est digne d&#8217;un Nelson (ou d&#8217;un ubermuda) en pleine forme. </p>
<p>Maintenant que c&#8217;est fait, nous allons parler ici d&#8217;un des aspects de symfony les plus intéressant, mais aussi d&#8217;un des moins bien documenté. Pas que les forms &laquo;&nbsp;basiques&nbsp;&raquo; ne soient pas documentés, mais dès que vous voulez faire autre chose qu&#8217;un form qui représente tel quel (ou presque) un objet de la base de données, ça se complique un peu. </p>
<p>Je vais essayer de vous faire un petit tour d&#8217;horizon de <strong>ce qu&#8217;on peut faire avec les embedded forms</strong>.<br />
<span id="more-896"></span></p>
<h3>Les forms, c&#8217;est de la POO</h3>
<p>Vous allez me dire que tout symfony est de la POO (Programmation Orientée Objet), et vous avez raison. Mais on peut utiliser symfony sans être un expert de la POO (voire sans vraiment savoir ce que c&#8217;est), en suivant Jobeet et en comprenant que executeIndex inclut le template indexSuccess.php en cas de succès. À partir de là, on est le roi du monde. </p>
<p>Enfin, du monde, moins les forms. Car les forms sont &#8211; à mon sens &#8211; la partie de symfony qui est à la fois utilisée par tous les utilisateurs de symfony, et à la fois<strong> très orientée objet </strong>(les factories sont par exemple beaucoup moins utilisées &#8230;). Du coup, ça mérite quelques explications.</p>
<h3>Tout le monde n&#8217;est pas doué en POO</h3>
<p>Certes tout le monde n&#8217;est pas doué en POO, professionnel ou pas d&#8217;ailleurs ;) À l&#8217;inverse de JAVA où les programmeurs n&#8217;ont pas vraiment le choix, la POO est récente dans PHP et certains l&#8217;ont découvert comme un cheveu sur la soupe. </p>
<p>L&#8217;impression que l&#8217;on a quand on essaye de changer le comportement des embed form c&#8217;est : mais <strong>où est-ce que je peux bien faire ce que j&#8217;ai envie de faire</strong>? doBind, doUpdateObject, saveEmbeddedForms, &#8230; les choix sont multiples. </p>
<p>Et pour cause, les forms utilisent énormément l&#8217;héritage, et disposent donc de moultes méthodes que l&#8217;on peut surcharger, sans que ce ne soit vraiment documenté autre part que dans l&#8217;API. Si on ne comprend pas les principes de base de la POO, les forms sont une vraie galère (déjà que quand on les comprend &#8230;). </p>
<p>Petit rappel sur la POO sur <a href="http://hdd34.developpez.com/cours/artpoo/ ">developpez.com</a> : Consultez au moins la partie sur l&#8217;héritage pour ceux qui sont largués.</p>
<h3>Principales étapes de gestion des formulaires</h3>
<p>Il faut quand même que vous ayez quelque chose à l&#8217;esprit, il y a pour moi deux principales étapes quand vous souhaitez traiter un formulaire qui vient d&#8217;être soumis :</p>
<ul>
<li><strong>1 ère étape</strong> : lier les valeurs qui sont récupérées dans la requête à l&#8217;objet Form (c&#8217;est à dire les recopier en s&#8217;assurant qu&#8217;elles sont valides et nettoyées)</li>
<li><strong>2 ème étape</strong> : Une fois les valeurs recopiés dans le $values du formulaire (qui n&#8217;est en fait qu&#8217;un tableau de valeurs nettoyées), réaliser un traitement dessus ou sur l&#8217;objet qui en découlera (nous verrons cela par la suite)</li>
</ul>
<p>
Il est toujours bon de <strong>se demander ce que l&#8217;on veut faire exactement</strong> : agir sur les valeurs qui seront recopiées et validées dans notre form, ou alors agir sur les valeurs nettoyées et recopiées, avant qu&#8217;elles ne soient sauvegardées en base.
</p>
<h4>Première étape</h4>
<p>Lors de la première étape de liaison/validation/recopie des valeurs de la requête dans votre formulaire (votre objet Form), vous pourrez agir sur les données<strong> avant qu&#8217;elles ne soient passées dans les validateurs</strong>, ou alors une fois qu&#8217;elles sont passées dans les validateurs.</p>
<p>Vous pourrez faire votre bourrin : enlever des parties de votre formulaire de la requête en vous basant sur certaines valeurs soumises.<br />
Par exemple enlever tout un formulaire embarqué, si certaines valeurs ne sont pas saisies (c&#8217;est l&#8217;exemple qu&#8217;on trouve pas mal sur le web d&#8217;ailleurs).<br />
Petit souci : les valeurs de la requête n&#8217;auront pas été validées/nettoyées par les validateurs, vous vous exposez donc à quelques soucis (dans le style le gars qui n&#8217;aura rempli que des espaces dans un champ texte, faudra penser à faire vous même le trim avant de vérifier la valeur &#8230;).<br />
<h4>Deuxième étape</h4>
<p>
Lors de la deuxième étape, c&#8217;est ici que vous devrez effectuer<strong> les traitements concernant votre logique métier</strong> à proprement parler. La première étape s&#8217;est occupée de vous donner accès aux valeurs dans votre formulaire (via le tableau $values), maintenant que c&#8217;est fait vous pouvez jouer avec.</p>
<p>
Vous voulez mettre à jour un objet qui dépend de celui que vous allez sauvegarder automatiquement, vous souhaitez insérer votre objet dans un nested set, etc etc &#8230; C&#8217;est dans cette étape que vous ferez ça.
</p>
<p>
Je sépare sciemment le processus en deux étapes, après vous pouvez le voir autrement. Les étapes étant chaînées, vous pouvez les mélanger et faire un peu de business logic à la fin de l&#8217;étape 1 par exemple, mais essayez de rester constant partout dans votre code, où vous ne saurez plus où aller voir quand il y a un souci.
</p>
<h3>Étude de cas</h3>
<p>On va prendre ici un cas tout simple, celui d&#8217;un formulaire auto-généré par doctrine à partir d&#8217;une table de votre modèle. </p>
<p>Appelons notre modèle PetitSuisse. On aura donc une classe nommée <code>PetitSuisseForm.class.php dans lib/form/doctrine/</code></p>
<p>On va vite fait faire le parcours d&#8217;héritage de cette classe :<br />
<strong>PetitSuisseForm => BasePetitSuisseForm => BaseFormDoctrine => sfFormDoctrine => sfFormObject => BaseForm => sfFormSymfony => sfForm (ouf)</strong>
</p>
<p>
Même si quelques unes de ces classes sont vides, on comprend que ça puisse devenir un peu dur de savoir où chercher. Alors oui <strong>l&#8217;abstraction c&#8217;est bon</strong> mangez en, mais ça engendre une complexité de lecture du code non négligeable.</p>
<p>Les classes qui sont &laquo;&nbsp;à vous&nbsp;&raquo; et donc dans lesquelles vous pourrez surcharger des méthodes sont <strong>PetitSuisseForm</strong> et <strong>BaseFormDoctrine</strong>.</p>
<p> La première vous permettra de surcharger une méthode relative au formulaire du modèle PetitSuisse, la deuxième vous permettra de surcharger de manière plus globale (chacun des forms auto-générés par doctrine héritant de BaseFormDoctrine). Je parle ici de symfony 1.3+, vous n&#8217;aurez pas toutes ces classes dans les versions précédentes.</p>
<p>On va maintenant parler <strong>des principales méthodes</strong> que vous aurez (peut être) à surcharger. Il en existe d&#8217;autres, si celles ci ne vous vont pas, lisez le code de symfony ;-)
</p>
<h3>1 ère étape (liaison)</h3>
<h4>doBind(array $values)</h4>
<p>
C&#8217;est la méthode qui va faire appel aux validateurs pour <strong>nettoyer les valeurs</strong> de la requête avant de les placer dans le tableau des valeurs. Ce sont ensuite sur ces valeurs nettoyées que nous travaillerons. Vous pouvez ici toucher aux données brut de pomme <strong>qui sortent directement du formulaire envoyé</strong>, sans traitement préalable.
</p>
<h3>2 ème étape (sauvegarde)</h3>
<h4>doSave($con = null)</h4>
<p>
C&#8217;est cette méthode qui sera appelée (par save() ) lorsque vous demandez la sauvegarde de votre formulaire. Elle se charge de mettre à jour votre objet avec les valeurs du form en appelant updateObject (qui appelera doUpdateObject que nous verrons par la suite).<br />
Si vous souhaitez <strong>changer le processus de sauvegarde</strong>, ajouter par exemple l&#8217;appel d&#8217;une vos méthodes à chaque sauvegarde du formulaire (pourquoi pas garder trace dans un fichier de toutes les sauvegardes de vos formulaires), c&#8217;est ici que vous devez le faire.
</p>
<h4>processValues($values)</h4>
<p>
C&#8217;est ici que vous pouvez<strong> toucher les valeurs qui ont été nettoyées </strong>par les validateurs, avant qu&#8217;elles ne soient passées à la méthode updateObject (que nous verrons par la suite). Je n&#8217;ai pas de traitement particulier en tête, mais l&#8217;idée est là : modifier des valeurs avant qu&#8217;elles ne soient utilisées pour mettre votre objet à jour.
</p>
<h4>doUpdateObject($values)</h4>
<p>
C&#8217;est cette méthode que vous devrez surcharger si vous voulez réaliser <strong>une opération spéciale sur votre objet avant qu&#8217;il ne soit enregistré</strong> dans la base.<br />
Par exemple, si vous voulez insérer cet objet à la fin d&#8217;un NestedSet, vous ferez ça ici. Le $values passé en paramètre est un tableau contenant les valeurs du formulaire, une fois qu&#8217;elles ont été nettoyées/vérifiées par les validateurs.
</p>
<h4>updateObjectEmbeddedForm($values)</h4>
<p>
Je pense qu&#8217;elle veut bien dire ce qu&#8217;elle veut dire. Elle va se charger d&#8217;appeler la fonction updateObject de chaque formulaire embarqué. Vous voulez agir sur le traitement des données par vos forms embarqués ? C&#8217;est par ici !
</p>
<h4>saveEmbeddedForms($con = null, $forms = null)</h4>
<p>
Allez la petite dernière pour la route : elle se charge de la sauvegarde de chacun des objets de vos forms embarqués. Fabien Potencier en donne un exemple de surcharge dans le livre <a  href="http://www.symfony-project.org/more-with-symfony/1_4/en/06-Advanced-Forms#chapter_06_sub_creating_a_custom_validator">&laquo;&nbsp;More with symfony&nbsp;&raquo;</a>
</p>
<h3>Exemple d&#8217;utilisation</h3>
<p>C&#8217;est bien beau de parler, mais un peu de concret ne fait pas de mal. </p>
<p>Vous trouverez un bon exemple pour comprendre le comportement des forms sur le blog de n1k0 dans son article <a  href="http://prendreuncafe.com/blog/post/2009/11/29/Embedding-Relations-in-Forms-with-Symfony-1.3-and-Doctrine">Embedding Relations in Forms with Symfony 1.3 and Doctrine</a>. Certes c&#8217;est en anglais, mais le code est universel ! Si vous avez des bons articles en français je suis preneur aussi. </p>
<p>Il existe maintenant la méthode embedRelation() et le plugin de Daniel Lohse <a  href="http://github.com/annismckenzie/ahDoctrineEasyEmbeddedRelationsPlugin">ahDoctrineEasyEmbeddedRelationsPlugin</a> qui font ça tout seul. Mais c&#8217;est toujours bien de comprendre ce que l&#8217;on fait ;-)</p>
<h3>Conclusion</h3>
<p>Je vous ai fait un petit tour d&#8217;horizon des principales méthodes que vous pouvez surcharger dans votre formulaire. Comme je l&#8217;ai dit plus haut, ce n&#8217;est pas une liste exhaustive, mais vous devriez avoir de quoi vous amuser. </p>
<p>J&#8217;ai juste essayé de voir tout cela d&#8217;une manière un peu plus globale et de ne pas proposer un n-ième exemple de code pour les formulaires embarqués. </p>
<p>Mais avec tout cet attirail, gardez à l&#8217;esprit que votre code d&#8217;action ne doit pas dépasser le classique <code>Bind &gt; Save</code>! Ce qui est au traitement de votre formulaire, reste dans votre formulaire! Enjoy!</p>
<p><small>Crédit photo: http://www.flickr.com/photos/fromeyetopixel/2470999873/</small></p>
]]></content:encoded>
			<wfw:commentRss>http://www.amicalement-web.net/symfony-les-forms-et-les-forms-embarques/2010/03/08/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Une nouvelle recrue, parce qu&#8217;il le vaut bien</title>
		<link>http://www.amicalement-web.net/une-nouvelle-recrue-parce-quil-le-vaut-bien/2009/08/27/</link>
		<comments>http://www.amicalement-web.net/une-nouvelle-recrue-parce-quil-le-vaut-bien/2009/08/27/#comments</comments>
		<pubDate>Thu, 27 Aug 2009 16:25:37 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Actualité]]></category>
		<category><![CDATA[sécurité]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://www.amicalement-web.net/?p=677</guid>
		<description><![CDATA[Oyez oyez, un petit billet pour vous informer qu&#8217;à compter d&#8217;aujourd&#8217;hui et pour une durée indéterminée, nous allons accueillir un petit nouveau sur ce blog! Bon ok, il est pas si nouveau que ça, vu qu&#8217;il traine dans les couloirs du site depuis quelques temps déjà et dans mes pattes encore plus :D C&#8217;est donc [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.amicalement-web.net/wp-content/uploads/image-mystere.png" alt="image-mystere" title="image-mystere" width="614" height="100" class="alignnone size-full wp-image-678" /> Oyez oyez, un petit billet pour vous informer qu&#8217;à compter d&#8217;aujourd&#8217;hui et pour une durée indéterminée, nous allons accueillir un petit nouveau sur ce blog! Bon ok, il est pas si nouveau que ça, vu qu&#8217;il traine dans les couloirs du site depuis quelques temps déjà et dans mes pattes encore plus :D<br />
<span id="more-677"></span></p>
<p>C&#8217;est donc mogito qui me rejoint pour écrire quelques beaux tutorials! Encore un Symfoniste? Et non, même pas, mogito est ce qu&#8217;on appelle un développeur&#8230; undergroud. Un homme de l&#8217;ombre qui est passionné de sécurité informatique et qui met ses compétences de développement pour cette passion principalement. En même temps, ça tombe bien, c&#8217;est aussi un peu son boulot, pour un gros site marchand qu&#8217;on ne citera pas (enfin je laisse mog le faire :p).</p>
<p>Vous aurez donc, dès demain, le droit à son premier tutorial. Je veux pas spoiler, mais ca parle de &#8230; sécurité informatique je crois!</p>
<p>J&#8217;espère que vous lui réservez un accueil chaleureux, et que vous apprécierez cet élargissement de la ligne éditoriale.</p>
<p>J&#8217;en profite pour relancer mon appel, je ne suis fermé à aucune proposition de collaboration! Plus on est de fous, plus on ris.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.amicalement-web.net/une-nouvelle-recrue-parce-quil-le-vaut-bien/2009/08/27/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Doctrine et son Behavior Geographical</title>
		<link>http://www.amicalement-web.net/doctrine-et-son-behavior-geographical/2009/08/05/</link>
		<comments>http://www.amicalement-web.net/doctrine-et-son-behavior-geographical/2009/08/05/#comments</comments>
		<pubDate>Wed, 05 Aug 2009 12:00:18 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[behavior]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[Symfony]]></category>

		<guid isPermaLink="false">http://www.amicalement-web.net/?p=485</guid>
		<description><![CDATA[Retour un peu à la technique, mais n&#8217;oubliez pas, il vous reste seulement jusqu&#8217;à demain pour gagner une BD. En ce qui nous concerne, avec les annonces faites lors de la dernière Symfony Conference Live, il faut bien commencer à se lancer dans Doctrine. Et c&#8217;est l&#8217;occasion de découvrir petit à petit cet ORM qui [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.amicalement-web.net/wp-content/uploads/edit-cut.png" alt="edit-cut" title="edit-cut" width="48" height="48" class="alignleft size-full wp-image-34" /> Retour un peu à la technique, mais n&#8217;oubliez pas, il vous reste seulement <a  href="/6-mois-deja-bilan-passe-objectifs-futurs-et-des-cadeaux/2009/07/31/">jusqu&#8217;à demain pour gagner une BD</a>.</p>
<p>En ce qui nous concerne, avec les annonces faites lors de la dernière Symfony Conference Live, il faut bien commencer à se lancer dans Doctrine. Et c&#8217;est l&#8217;occasion de découvrir petit à petit cet ORM qui change beaucoup de Propel, pas toujours en bien à mon goût, mais il y a quand même des choses très bien pensées.<br />
En parcourant la <a  class="extern" href="http://www.doctrine-project.org/documentation">doc</a>, je suis donc tombé sur un behavior que j&#8217;ai trouvé fort intéressant: <a  class="extern" href="http://www.doctrine-project.org/documentation/manual/1_1/en/behaviors#core-behaviors:geographical">Geographical</a>.</p>
<p>Je me suis donc laissé tenté par un test de celui-ci!<br />
<span id="more-485"></span><br />
Le principe est simple, il rajoute 2 champs à notre table, latitude et longitude, qui outre le fait de donner les coordonnées GPS de notre adresse, pouvant ainsi alimenter une googlemap, permet également de faire des calculs de distance (à vol d&#8217;oiseau évidemment).<br />
Et d&#8217;ailleurs apparemment, c&#8217;est sa seule fonction, car le remplissage des champs est à notre charge. Mais ça reste une fonctionnalité intéressante.</p>
<p>On se lance donc dans une application à un projet, symfony bien évidemment, avec une table classique d&#8217;adresse avec le behavior de déclaré.<br />
Voici notre schema.yml</p>

<div class="wp_syntax"><div class="code"><pre class="yaml" style="font-family:monospace;">adresses:
  actAs: [ Geographical]
  tableName: adresses
  columns:
    aid: { type: integer(4), primary: true,  autoincrement: true }
    adresse1: { type: string(255), notnull: true }
    adresse2: { type: string(255) }
    cp: { type: string(5), notnull: true }
    ville: { type: string(100), notnull: true }</pre></div></div>

<p>Ce qui donne en base une fois notre table créé:</p>
<pre>+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| aid        | int(11)      | NO   | PRI | NULL    | auto_increment |
| adresse1   | varchar(255) | NO   |     | NULL    |                |
| adresse2   | varchar(255) | YES  |     | NULL    |                |
| cp         | varchar(5)   | NO   |     | NULL    |                |
| ville      | varchar(100) | NO   |     | NULL    |                |
| latitude   | double       | YES  |     | NULL    |                |
| longitude  | double       | YES  |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+</pre>
<p>Maintenant que les bases sont posées, on va créer une petite classe rapide qui va nous permettre d&#8217;alimenter les champs latitude et longitude de notre table. Pour cela on va se servir de l&#8217;inévitable googlemap. Il vous faudra pour cela une clé pour utiliser leur API.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">class</span> Geoloc
<span style="color: #009900;">&#123;</span>
  <span style="color: #000000; font-weight: bold;">protected</span> 
    <span style="color: #000088;">$adr</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">,</span>
    <span style="color: #000088;">$key</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">,</span>
    <span style="color: #000088;">$url</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">,</span>
    <span style="color: #000088;">$context</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">,</span>
    <span style="color: #000088;">$format</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">,</span>
    <span style="color: #000088;">$x</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">,</span>
    <span style="color: #000088;">$y</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> execute<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
  <span style="color: #009900;">&#123;</span>
    <span style="color: #666666; font-style: italic;">// On utilise un stream_context pour avoir la main sur certaines options plus facilement, comme le timeout de notre connexion</span>
    <span style="color: #000088;">$context</span> <span style="color: #339933;">=</span> <span style="color: #990000;">stream_context_create</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
      <span style="color: #0000ff;">'http'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
        <span style="color: #0000ff;">'method'</span>  <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'GET'</span><span style="color: #339933;">,</span>
        <span style="color: #0000ff;">'header'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">&quot;Content-type: application/x-www-form-urlencoded<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">,</span>
        <span style="color: #0000ff;">'timeout'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #cc66cc;">5</span><span style="color: #339933;">,</span>
      <span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
    <span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// Google n'autorise que les requêtes en GET, on construit donc notre liste de paramètre prêt à emploi</span>
    <span style="color: #000088;">$param</span> <span style="color: #339933;">=</span> <span style="color: #990000;">http_build_query</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
          <span style="color: #0000ff;">'q'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">adr</span><span style="color: #339933;">,</span>
          <span style="color: #0000ff;">'output'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">format</span><span style="color: #339933;">,</span>
          <span style="color: #0000ff;">'oe'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'utf8'</span><span style="color: #339933;">,</span>
          <span style="color: #0000ff;">'sensor'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'false'</span><span style="color: #339933;">,</span>
          <span style="color: #0000ff;">'key'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">key</span>
    <span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000088;">$content</span> <span style="color: #339933;">=</span> <span style="color: #990000;">file_get_contents</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">url</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'?'</span><span style="color: #339933;">.</span><span style="color: #000088;">$param</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">,</span> <span style="color: #000088;">$context</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$content</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
      <span style="color: #666666; font-style: italic;">// Le traitement est prévu ici pour du csv</span>
      <span style="color: #000088;">$retour</span> <span style="color: #339933;">=</span> <span style="color: #990000;">explode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">','</span><span style="color: #339933;">,</span><span style="color: #000088;">$content</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
      <span style="color: #666666; font-style: italic;">// Si le retour est bon, on récupère les coordonnées</span>
      <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$retour</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">==</span> <span style="color: #0000ff;">'200'</span><span style="color: #009900;">&#41;</span>
      <span style="color: #009900;">&#123;</span>
        <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">x</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$retour</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">2</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">y</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$retour</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">3</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #b1b100;">return</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">x</span><span style="color: #339933;">,</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">y</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> <span style="color: #009900; font-weight: bold;">false</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> getX<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
  <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">x</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
&nbsp;
  <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> getY<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
  <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">y</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Voilà, il suffit de placer cette classe dans un dossier Lib de Symfony, où on bon vous semble. Maintenant on va créer dans notre fichier adresses.class.php 2 méthodes:<br />
- la première qui lancera la mise à jour des champs via google<br />
- la deuxième qui automatisera cette mise à jour, lors d&#8217;une nouvelle insertion et seulement dans ce cas. (et non pas à chaque mise à jour)</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> updateCoordonnees<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$completeAdr</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;;</span>adresse1<span style="color: #339933;">.</span><span style="color: #0000ff;">' '</span><span style="color: #339933;">.</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">empty</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">adresse2</span><span style="color: #009900;">&#41;</span>?<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">adresse2</span><span style="color: #339933;">.</span><span style="color: #0000ff;">' '</span><span style="color: #339933;">.:</span><span style="color: #0000ff;">''</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cp</span><span style="color: #339933;">.</span><span style="color: #0000ff;">' '</span><span style="color: #339933;">.</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">ville</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// Ajout adresse2 seulement si non vide</span>
    <span style="color: #000088;">$geoloc</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Geoloc<span style="color: #009900;">&#40;</span><span style="color: #000088;">$completeAdr</span><span style="color: #339933;">,</span><span style="color: #0000ff;">'maCleGoogleMap'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$data</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$geoloc</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">execute</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
      <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">latitude</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$data</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
      <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">longitude</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$data</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #009933; font-style: italic;">/**
 *  Méthode appelé avant l'insertion d'un nouvel élément. Elle est à surcharger au besoin.
 */</span>
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> preInsert<span style="color: #009900;">&#40;</span><span style="color: #000088;">$event</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
  <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">updateCoordonnees</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Et voilà, avec un fichier de fixtures.yml du genre:</p>

<div class="wp_syntax"><div class="code"><pre class="yaml" style="font-family:monospace;">adresses:
  adresse_1:
    adresse1: 165 avenue du prado
    cp: 13008
    ville: Marseille
  adresse_2:
    adresse1: 7 rue de verdun
    cp: 13005
    ville: Marseille
  adresse_3:
    adresse1: 57 Bd Romain Rolland 
    cp: 13010
    ville: Marseille
  adresse_4:
    adresse1: Route de Gemenos 
    cp: 13400
    ville: Aubagne</pre></div></div>

<p>En le chargeant via symfony, on obtient en base:</p>
<pre>
select * from adresses ;
+-----+----------------------+----------+-------+-----------+------------+-----------+
| aid | adresse1             | address2 | cp    | ville     | latitude   | longitude |
+-----+----------------------+----------+-------+-----------+------------+-----------+
|   1 | 165 avenue du prado  | NULL   | 13008 | Marseille | 43.2783515 | 5.3883703 |
|   2 | 7 rue de verdun      | NULL     | 13005 | Marseille |  43.296542 | 5.3958405 |
|   3 | 57 Bd Romain Rolland | NULL     | 13010 | Marseille | 43.2774579 | 5.4202946 |
|   4 | Route de Gemenos     | NULL     | 13400 | Aubagne  | 43.2975544 | 5.5888222 |
+-----+----------------------+----------+-------+-----------+------------+-----------+
</pre>
<p>Un petit bout de code dans une action, rapidement pour tester:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> executeTest<span style="color: #009900;">&#40;</span>sfWebRequest <span style="color: #000088;">$request</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$adr1</span> <span style="color: #339933;">=</span> Doctrine<span style="color: #339933;">::</span><span style="color: #004000;">getTable</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'adresses'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$adr2</span> <span style="color: #339933;">=</span> Doctrine<span style="color: #339933;">::</span><span style="color: #004000;">getTable</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'adresses'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">2</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">echo</span> <span style="color: #990000;">number_format</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$adr1</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getDistance</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$adr2</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span><span style="color: #cc66cc;">2</span><span style="color: #339933;">,</span><span style="color: #0000ff;">','</span><span style="color: #339933;">,</span><span style="color: #0000ff;">''</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'Km'</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// Par défaut, retourne en miles, il faut mettre le 2e paramètre à true pour avoir le résultat en km</span>
&nbsp;
    <span style="color: #b1b100;">return</span> sfView<span style="color: #339933;">::</span><span style="color: #004000;">NONE</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>Et on obtient la distance en km entre nos 2 premières adresses. (Ici la distance entre chez moi et mon boulot :p)<br />
Comme l&#8217;indique la doc, on peut aller plus loin, en récupérant pour une adresse donnée, les N adresses les plus proches, ce qui peut donner lieu à des fonctionnalités très intéressantes:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> executeTest2<span style="color: #009900;">&#40;</span>sfWebRequest <span style="color: #000088;">$request</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #000088;">$adr1</span> <span style="color: #339933;">=</span> Doctrine<span style="color: #339933;">::</span><span style="color: #004000;">getTable</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'adresses'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">find</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$q</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$adr1</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getDistanceQuery</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$result</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$q</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">orderby</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'miles asc'</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">// On les trie par distance</span>
              <span style="color: #339933;">-&gt;</span><span style="color: #004000;">addWhere</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$q</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getRootAlias</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span> <span style="color: #0000ff;">'.aid != ?'</span><span style="color: #339933;">,</span><span style="color: #000088;">$adr1</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">aid</span><span style="color: #009900;">&#41;</span> <span style="color: #666666; font-style: italic;">// On exclut l'adresse de référence</span>
              <span style="color: #339933;">-&gt;</span><span style="color: #004000;">execute</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$result</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$res</span><span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
       <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$res</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cp</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot; - &quot;</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$res</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">kilometers</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot;&lt;br/&gt;&quot;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> sfView<span style="color: #339933;">::</span><span style="color: #004000;">NONE</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span></pre></div></div>

<p>A noter que le code du behavior est très simple en fait, il ajoute juste une formule mathématique pour ces calculs de distance. Mais c&#8217;est ce genre de petites fonctionnalités qui me font apprécier la découverte de Doctrine!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.amicalement-web.net/doctrine-et-son-behavior-geographical/2009/08/05/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
<!-- WP Super Cache is installed but broken. The path to wp-cache-phase1.php in wp-content/advanced-cache.php must be fixed! -->
