{"action":"created","comment":{"url":"https://api.github.com/repos/afsy/website/pulls/comments/155957079","pull_request_review_id":82358577,"id":155957079,"diff_hunk":"@@ -0,0 +1,508 @@\n+{% extends 'AfsyFrontBundle:Avent:day.html.twig' %}\n+\n+{% set year = 2017 %}\n+\n+{% block article_title \"Jour 11 - Non à la Doctrine, découvrez Pomm et (re-)découvrez PostgreSQL\" %}\n+\n+{% block article_content %}\n+    <h1>Non à la Doctrine, découvrez Pomm et (re-)découvrez PostgreSQL</h1>\n+    Sommaire :\n+    <ul>\n+        <li><a href=\"#introduction\">Introduction</a></li>\n+        <li><a href=\"#pommcestquoi\">Pomm, c'est quoi ?</a></li>\n+        <li><a href=\"#presentation\">Foundation, ModelManager et Cli sont dans un bateau</a></li>\n+        <li><a href=\"#pommetsymfony\">Pomm et symfony</a></li>\n+        <li><a href=\"#context\">Contexte et préparation</a></li>\n+        <li><a href=\"#crud\">CRUD</a></li>\n+        <li><a href=\"#liaison\">Liaison multiple</a></li>\n+        <li><a href=\"#multilingue\">Gestion du multilingue</a></li>\n+        <li><a href=\"#statistiques\">Statistiques</a></li>\n+        <li><a href=\"#conclusion\">Conclusion</a></li>\n+        <li><a href=\"#bibliographie\">Bibliographie & Annexes</a></li>\n+    </ul>\n+\n+    <h2 id=\"introduction\">Introduction</h2>\n+    <p>Aujourd'hui, lorsque l'on parle des bases de données, nous pensons tout de suite ORM.\n+        Le plus célèbre d'entre eux est sans doute Doctrine.<br/>\n+        <b>Mais à quoi sert l'ORM ?</b><br/>\n+    </p>\n+    <p>\n+        Le but premier de celui-ci est l'abstraction de la base de données. En effet, nul besoin de connaissances en SQL pour persister ou récupérer des données.<br/>\n+        Celui-ci permet également de changer facilement de SGBD. Cependant, qui, dans la vie d'un projet, a besoin de ça sans refonte ?<br/><br/>\n+        Le constat est qu'aujourd'hui, si bon nombre de développeurs font du Symfony avant de faire du PHP, ils sont encore plus nombreux à faire du Doctrine avant de faire du SQL.<br/>\n+        Ce qui était, avant, le point de départ d'un projet, est devenu complètement ignoré aujourd'hui.<br/>\n+        De plus nous assistons, de nos jours à des courants qui mettent le métier au centre du développement (DDD, Clean Architecture etc..). La base de données exprime \"le métier\",\n+        et ne pas maîtriser celle-ci va donc à l'encontre de ces courants.<br/>\n+        <br/>\n+        Dans cette introduction, il me reste un faux combat à résoudre : MySql Vs PostgreSQL. De nombreux articles traitent déjà le sujet depuis bien longtemps.\n+        Si vous êtes malgré tout persuadés que MySQL peut rivaliser avec PostgreSQL, nous allons découvrir dans cet article quelques concepts qui devraient vous convaincre du contraire.\n+    </p>\n+    <h2 id=\"pommcestquoi\">Pomm, c'est quoi?</h2>\n+    <p>\n+        Pomm est une alternative aux ORM. Il se définit comme un Object Model Manager.\n+        La principale ligne de conduite du projet est d'être la moins intrusif possible et la plus simple. Ainsi, vous l'oublierez rapidement pour vous concentrer sur PostgreSQL.\n+        Pomm vous permettra de reprendre le contrôle de votre SGBD et de comprendre ce qu'il s'y passe afin de gagner en performance et bien plus.\n+    </p>\n+\n+    <h2 id=\"presentation\">Foundation, ModelManager et Cli sont dans un bateau</h2>\n+    <h3>Foundation</h3>\n+    <p>\n+        C'est la brique principale qui permet de s'interfacer avec PostgreSQL. Elle est composée de sessions qui, elles-mêmes, embarquent des clients :\n+        <ul>\n+            <li>converter</li>\n+            <li>query_manager</li>\n+            <li>prepared_query</li>\n+            <li>observer</li>\n+            <li>notifier</li>\n+            <li>inspector</li>\n+        </ul>\n+    </p>\n+    <h3>ModelManager</h3>\n+    <p>Cette brique est une extension de la première, c'est un client de la session au même titre que les précédents. Ce client apporte la modélisation objet de la base.\n+    </p>\n+    <h3>Cli</h3>\n+    <p>\n+        Il permet de nous faciliter la vie en générant les différents objets pour le <code class=\"language-none\">ModelManager</code> par exemple. Des commandes d'inspections sont également disponibles pour éviter d'ouvrir une console psql.\n+    </p>\n+\n+    <h2 id=\"pommetsymfony\">Pomm et Symfony</h2>\n+    <p>\n+        Pomm propose un bundle pour faciliter son intégration dans un projet Symfony : <a href=\"https://github.com/pomm-project/pomm-bundle\" target=\"_blank\">https://github.com/pomm-project/pomm-bundle</a>\n+        <br/>\n+        Celui-ci respecte également le principe de Pomm, être le moins intrusif, le plus transparent et simple.\n+        <br/>\n+        Il intègre tout ce dont on a besoin pour développer avec Symfony, l'accès au profiler, les commandes et les configurations en yaml.<br/>\n+    </p>\n+\n+    <h2 id=\"context\">Contexte et préparation</h2>\n+    <p>\n+        Le but de ce tutoriel sera de créer un mini système d'évévenements et de réservations. Cela nous permettra de passer en revue des concepts PostgreSQL et Pomm.<br/>\n+        Pour la suite de l'article j'essaierai de ne pas complexifier l'application pour me concentrer sur l'essentiel du sujet.<br/>\n+        <a href=\"https://github.com/stood/evenement\" target=\"_blank\">Repo. Github du tutoriel</a>\n+    </p>\n+    <h3>Installation Rapide</h3>\n+\n+    <p>\n+        J'utilise la version 3.4 de symfony pour ce tutoriel.\n+        <pre><code class=\"language-sh\">composer require pomm-project/pomm-bundle</code></pre>\n+        Avec Flex, le reste se fait tout seul. Pour ceux qui n'utilisent pas Flex nous restons sur les classiques d'ajout au Kernel etc. Tout est expliqué ici : <a href=\"https://github.com/pomm-project/pomm-bundle#setup\" target=\"_blank\">Setup Pomm-Bundle</a>\n+        <br/>\n+        (Pour plus d'infos sur Flex, je vous invite à regarder l'article de Loick Piera : <a href=\"https://afsy.fr/avent/2017/08-symfony-flex-la-nouvelle-facon-de-developper-avec-symfony\">ici</a>.)\n+        <br/>\n+        Le fichier de config :\n+        <pre><code class=\"language-yaml\">pomm:\n+    configuration:\n+        db:\n+            dsn: \"%env(DATABASE_URL)%\"\n+            session_builder: App\\SessionBuilder\\DbSessionBuilder</code></pre>\n+        <ul>\n+            <li>'db' réprésente le nom de la session que vous utiliserez dans l'application. Il est possible d'en créer autant que nous le souhaitons. Cela permet par exemple d'avoir différents accès à la base avec des identifiants différents.</li>\n+            <li>'dsn' c'est notre définition de connexion</li>\n+            <li>'session_builder' est optionnel mais cela permet de définir les Clients de l'application (<a href=\"https://github.com/pomm-project/Foundation/blob/master/documentation/foundation.rst#client-pooler\" target=\"_blank\">voir la doc</a>) mais également\n+                les converters.<br/>\n+                J'ai pour habitude de créer systématiquement un SessionBuilder car c'est le point d'entrée. Ici, j'ai ajouté un Converter HSTORE car il provient\n+                d'une extension PostgreSQL et n'est donc pas déclaré par défaut.\n+                <pre><code class=\"language-php\">class DbSessionBuilder extends SessionBuilder\n+{\n+    protected function initializeConverterHolder(ConverterHolder $converter_holder)\n+    {\n+        parent::initializeConverterHolder($converter_holder);\n+\n+        $converter_holder\n+            ->registerConverter('Hstore', new PgHstore(), ['public.hstore']);\n+    }\n+}</code></pre>\n+            </li>\n+        </ul>\n+    </p>\n+    <h3>Création de la base</h3>\n+    <p>\n+        Contrairement aux ORM, Pomm ne construit pas la base de données depuis des fichiers xml ou grâce à des annotations. La principale raison ?\n+        PostgreSQL est le meilleur outil pour faire cela et il prend en compte des éléments ignorés par Pomm ( ACL, tablespaces etc.)\n+        <br/>\n+        Cela vous permettra également de gros gains, en pouvant créer, par exemple, des index.\n+        Voici le fichier SQL utilisé pour la création de la base qui nous servira pour le tutoriel :\n+        <a href=\"https://github.com/stood/evenement/blob/master/sql/database.sql\" target=\"_blank\">database.sql</a>\n+    </p>\n+    <p>\n+        <b>Utilisation des schémas</b>\n+        <pre><code class=\"sql\">CREATE SCHEMA application;</code></pre>\n+        Pour ceux qui ne connaissent pas les schémas, cela permet d'organiser vos tables en groupes logiques <br/>(exit les préfixes de table 😛).<br/>\n+        Cela permet également de pouvoir attribuer des droits spécifiques à des utilisateurs sans descendre au niveau des tables et d'avoir une logique métier.\n+        <a href=\"https://docs.postgresql.fr/9.5/ddl-schemas.html\" target=\"_blank\">Voir la doc sur les schémas </a>\n+    </p>\n+    <p>\n+        <b>Utilisation des types : HSTORE, JSONB, TSRANGE, POINT</b><br/>\n+        Ce sont des types disponibles dans PostgreSQL qui viennent enrichir ceux que vous connaissez déjà.<br/>\n+        <ul>\n+            <li>HSTORE : Permet de stocker des ensembles de paires clé/valeur.</li>\n+            <li>JSONB : Permet de stocker du JSONB.</li>\n+            <li>TSRANGE : Permet de stocker un intervalle de temps.</li>\n+            <li>POINT : Permet de stocker les coordonnées géographiques d'un point.</li>\n+        </ul>\n+        Afin de convertir ces types en php et vice versa, Pomm utilise des Converters.\n+        Pomm gère, quasi nativement, tous les types disponibles dans PostgreSQL.<br/>\n+        Voici la liste des différents types disponibles dans PostgreSQL <a href=\"https://www.postgresql.org/docs/9.5/static/datatype.html\" target=\"_blank\">lien vers la doc</a>.<br/>\n+        Sachez que vous pouvez également créer vos propres types dans PostgreSQL et facilement déclarer ceux-ci dans Pomm.\n+    </p>\n+    <h3>Initialisation des fichiers</h3>\n+    <p>\n+        <pre><code class=\"language-sh\">bin/console pomm:generate:schema-all db application -d ./src -a 'App' --psr4</code></pre>\n+        Cette commande permettra de créer les fichiers nécessaires à Pomm depuis la base de données.<br/>\n+        <ul>\n+            <li>'db' : indique le nom de la session spécifiée dans le fichier \"pomm.yaml\"</li>\n+            <li>'application': nom du schéma</li>\n+        </ul>\n+\n+        La commande a créé 3 fichiers par tables :<br/>\n+        <img src=\"{{ asset('bundles/afsyfront/images/avent/2017/11/filesgenerate.png') }}\" class=\"img-article\"/>\n+        <ul>\n+            <li>Les <code class=\"language-none\">FlexibleEntity</code> : ce sont nos objets métiers qui seront le reflet de la projection de la requête.</li>\n+            <li>Les <code class=\"language-none\">Model</code> : ils permettent d'intéragir avec la base.</li>\n+            <li>Les <code class=\"language-none\">Structure</code> : c'est la définition de la table qui permettra à Pomm de mapper les types.</li>\n+        </ul>\n+    </p>\n+    <p style=\"clear: both;\">\n+        <b>La liste des commandes disponibles dans Pomm : </b><br/>\n+        <img src=\"{{ asset('bundles/afsyfront/images/avent/2017/11/listcmdpomm.png') }}\"/><br/>\n+        <b>Voila nous sommes ready, nous pouvons y aller !</b>\n+    </p>\n+    <h2 id=\"crud\">CRUD</h2>\n+    <h3>Récupération de la liste des événements</h3>\n+    <p>\n+        <pre><code class=\"language-php\">$events = $this->get('pomm')\n+                ->getDefaultSession()\n+                ->getModel(EventModel::class)\n+                ->findAll();</code></pre><br/>\n+        Ici, nous utilisons le Model Manager, qui nous met à disposition des fonctions de CRUD (<a href=\"https://github.com/pomm-project/ModelManager/blob/2.0/documentation/model_manager.rst#default-queries\" target=\"_blank\">voir les fonctions disponibles</a>).\n+        'findAll()' nous renvoie un iterator d'objets Event.\n+        <br/>\n+        Cependant, récupérer l'événement sans sa catégorie n'a que très peu d'intérêt. Nous allons donc enrichir la projection de notre requête et en profiter pour récupérer le nombre d'inscrits ainsi que le nombre de jours restants avant la fin de l'événement\n+        <pre><code class=\"language-php language-sql\">/* src/Db/ApplicationSchema/EventModel.php */\n+public function findWithCategoryAndNbRegister()\n+{\n+    $sql = <<< SQL\n+                    SELECT\n+                    :projection\n+                    FROM :event event\n+                    INNER JOIN :category category USING (category_id)\n+                    LEFT JOIN :register register USING (event_id)\n+                    GROUP BY event_id, category.*\n+                    SQL;\n+    $projection = $this->createProjection()\n+        ->setField('category', 'category', '\\App\\Db\\ApplicationSchema\\Category')\n+        ->setField('nb_register', 'count(register)', 'int4')\n+        ->setField('nb_day', 'extract(DAY FROM upper(event.timespan) - current_date)', 'int4');\n+\n+    $sql = strtr($sql,\n+        [\n+            ':projection' => $projection->formatFieldsWithFieldAlias('event'),\n+            ':event'      => $this->getStructure()->getRelation(),\n+            ':category'      => $this->getSession()\n+                ->getModel(CategoryModel::class)\n+                ->getStructure()\n+                ->getRelation(),\n+            ':register'      => $this->getSession()\n+                ->getModel(RegisterModel::class)\n+                ->getStructure()\n+                ->getRelation()\n+        ]\n+    );\n+\n+    return $this->query($sql, [], $projection);\n+}</code></pre>\n+    <pre><code class=\"language-php\">$events = $this->get('pomm')\n+                ->getDefaultSession()\n+                ->getModel(EventModel::class)\n+                ->findWithCategoryAndNbRegister();</code></pre><br/>\n+    </p>\n+    <p>\n+        Ici nous reprenons le contrôle en écrivant nous-même la requête bien que celle-ci est simple. Nous avons enrichi\n+        notre projection en lui ajoutant des champs. Cette fonction renvoie un iterator de FlexibleEntity.<br/>\n+        En ne maîtrisant pas correctement l'ORM que nous utilisons, il n'est pas rare que pour récupérer deux éléments liés, nous fassions deux requêtes sans même le savoir.\n+        Alors que sur des boucles qui présentent des listes, les requêtes sont multipliées par le nombre de résultats.\n+    </p>\n+    <table>\n+        <thead>\n+            <tr>\n+                <td class=\"text-center\">findAll()</td>\n+                <td class=\"text-center\" style=\"width: 10%;\"></td>\n+                <td class=\"text-center\">findWithCategoryAndNbRegister()</td>\n+            </tr>\n+        </thead>\n+        <tbody>\n+            <tr>\n+                <td class=\"text-center\" style=\"vertical-align: middle;\"><img style=\"display: inline;\" src=\"{{ asset('bundles/afsyfront/images/avent/2017/11/dumpflexiblefindall.png') }}\"/></td>\n+                <td class=\"text-center\" style=\"vertical-align: middle;\">=></td>\n+                <td class=\"text-center\" style=\"vertical-align: middle;\"><img style=\"display: inline;\" src=\"{{ asset('bundles/afsyfront/images/avent/2017/11/dumpflexible.png') }}\"/><br/></td>\n+            </tr>\n+        </tbody>\n+    </table>\n+    <p>\n+        C'est là que le nom de Flexible prend tout son sens. En effet, l'objet retourné est maitenant la représentation de cette nouvelle projection.\n+        Nous remarquons, au passage, les typages (DateTime, TsRange, Point, Array ...).<br/>\n+        C'est donc notre SGBD qui fait le travail, ou plutôt qui fait son travail en facilitant le nôtre côté PHP.\n+    </p>\n+    <h3>Enregistrement d'une inscription</h3>\n+    <pre><code class=\"language-php\">/* src/Controller/RegistrationController.php */\n+public function process(Request $request)\n+{\n+    $register = new Register([\n+        'event'     => null,\n+        'lastname'  => null,\n+        'firstname' => null,\n+        'email'     => null\n+    ]);\n+\n+    $form = $this->createForm(RegistrationType::class, $register);\n+    $form->handleRequest($request);\n+\n+    if ($form->isSubmitted() && $form->isValid()) {\n+        $this->get('pomm')\n+            ->getDefaultSession()\n+            ->getModel(RegisterModel::class)\n+            ->insertOne($register);\n+\n+        return $this->redirect($this->generateUrl('event_list'));\n+    }\n+\n+    return $this->render('registration/create.html.twig', array(\n+        'form' => $form->createView(),\n+    ));\n+}</code></pre>\n+    <p>\n+        Grâce au Model, je crée un FlexibleEntity de Register. Celui-ci étant vide, puisque sans projection, je lui\n+        fournis la liste des propriétés qui le composeront.<br/>\n+        Le module Form a donc tout ce qu'il faut pour pouvoir mapper le formulaire avec l'objet.\n+        Pour mon rattachement à l'événement j'utilise un Listener.<br/>\n+        N'ayant pas de Type spécifique pour Pomm, j'utilise le ChoiceList qui me permet d'indiquer les éléments à prendre en compte.\n+        <pre><code class=\"language-php\">/* src/Form/EventListener/AddEventFieldSubscriber.php */\n+public function preSetData(FormEvent $event)\n+{\n+    $form = $event->getForm();\n+\n+    $form->add('event', ChoiceType::class, [\n+        'label' => 'Evenement',\n+        'choices' => $this->getEvents(),\n+        'choice_label' => function($elt) {\n+            return $elt->getName()['fr'];\n+        },\n+        'placeholder' => 'Choisir un événement',\n+        'choice_value' => function($elt) {\n+            if ($elt !== null) {\n+                return $elt->getEventId();\n+            }\n+        },\n+        'required' => true,\n+        'attr' => ['class'=>'form-control']\n+    ]);\n+}\n+\n+public function postSubmit(FormEvent $event)\n+{\n+    $data = $event->getData();\n+\n+    if ($data->getEvent() != null) {\n+        $data->set('event_id', $data->getEvent()->getEventId());\n+    }\n+\n+    return;\n+}\n+\n+private function getEvents()\n+{\n+    return $this->pomm\n+        ->getDefaultSession()\n+        ->getModel(EventModel::class)\n+        ->findAll();\n+}</code></pre>\n+    </p>\n+    <h3>Edition d'un enregistrement</h3>\n+    <pre><code class=\"language-php\">/* src/Controller/EditRegistrationController.php */\n+$this->get('pomm')\n+    ->getDefaultSession()\n+    ->getModel(RegisterModel::class)\n+    ->updateOne(\n+        $register,\n+        ['lastname', 'firstname', 'email', 'event_id']\n+    );</code></pre>\n+    <p>\n+        Pour l'édition, pas de changements notables si ce n'est que nous spécifions les colonnes à mettre à jour. Dans la v2.1 du ModelManager, ce tableau\n+        sera optionnel, et seules les colonnes qui auront changé d'état seront mises à jour.<br/>\n+        Il est également possible d'utiliser les 'Param Converter' de Symfony pour récupérer l'élément (<a href=\"https://github.com/pomm-project/pomm-bundle#param-converter\" target=\"_blank\">Voir la doc</a>).\n+    </p>\n+    <h3>Suppression d'un enregistrement</h3>\n+    <pre><code class=\"language-php\">/* src/Controller/DeleteRegistrationController.php */\n+public function process(Request $request, $registerId)\n+{\n+    $register = $this->get('pomm')\n+        ->getDefaultSession()\n+        ->getModel(RegisterModel::class)\n+        ->deleteByPK(['register_id' => $registerId]);\n+\n+    return $this->redirect($this->generateUrl('event_registration_list'));","path":"src/Afsy/Bundle/FrontBundle/Resources/views/Avent/day_2017_11.html.twig","position":343,"original_position":343,"commit_id":"b1e58f01a9090bbcd55fd74c7e1fd5ad11cd2dc1","original_commit_id":"b1e58f01a9090bbcd55fd74c7e1fd5ad11cd2dc1","user":{"login":"hhamon","id":235550,"avatar_url":"https://avatars3.githubusercontent.com/u/235550?v=4","gravatar_id":"","url":"https://api.github.com/users/hhamon","html_url":"https://github.com/hhamon","followers_url":"https://api.github.com/users/hhamon/followers","following_url":"https://api.github.com/users/hhamon/following{/other_user}","gists_url":"https://api.github.com/users/hhamon/gists{/gist_id}","starred_url":"https://api.github.com/users/hhamon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hhamon/subscriptions","organizations_url":"https://api.github.com/users/hhamon/orgs","repos_url":"https://api.github.com/users/hhamon/repos","events_url":"https://api.github.com/users/hhamon/events{/privacy}","received_events_url":"https://api.github.com/users/hhamon/received_events","type":"User","site_admin":false},"body":"`return $this->redirectToRoute('event_registration_list');`","created_at":"2017-12-10T19:15:25Z","updated_at":"2017-12-10T19:20:06Z","html_url":"https://github.com/afsy/website/pull/113#discussion_r155957079","pull_request_url":"https://api.github.com/repos/afsy/website/pulls/113","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/afsy/website/pulls/comments/155957079"},"html":{"href":"https://github.com/afsy/website/pull/113#discussion_r155957079"},"pull_request":{"href":"https://api.github.com/repos/afsy/website/pulls/113"}}},"pull_request":{"url":"https://api.github.com/repos/afsy/website/pulls/113","id":157395443,"html_url":"https://github.com/afsy/website/pull/113","diff_url":"https://github.com/afsy/website/pull/113.diff","patch_url":"https://github.com/afsy/website/pull/113.patch","issue_url":"https://api.github.com/repos/afsy/website/issues/113","number":113,"state":"open","locked":false,"title":"Article pour Lundi 11","user":{"login":"stood","id":327248,"avatar_url":"https://avatars1.githubusercontent.com/u/327248?v=4","gravatar_id":"","url":"https://api.github.com/users/stood","html_url":"https://github.com/stood","followers_url":"https://api.github.com/users/stood/followers","following_url":"https://api.github.com/users/stood/following{/other_user}","gists_url":"https://api.github.com/users/stood/gists{/gist_id}","starred_url":"https://api.github.com/users/stood/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stood/subscriptions","organizations_url":"https://api.github.com/users/stood/orgs","repos_url":"https://api.github.com/users/stood/repos","events_url":"https://api.github.com/users/stood/events{/privacy}","received_events_url":"https://api.github.com/users/stood/received_events","type":"User","site_admin":false},"body":"![capturearticle](https://user-images.githubusercontent.com/327248/33797697-e450e2a6-dd0c-11e7-99e0-21153f07e492.png)\r\n","created_at":"2017-12-09T17:15:38Z","updated_at":"2017-12-10T19:20:06Z","closed_at":null,"merged_at":null,"merge_commit_sha":"267d2d4e84113364b4c8ce48b9e59743f47f0e2b","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/afsy/website/pulls/113/commits","review_comments_url":"https://api.github.com/repos/afsy/website/pulls/113/comments","review_comment_url":"https://api.github.com/repos/afsy/website/pulls/comments{/number}","comments_url":"https://api.github.com/repos/afsy/website/issues/113/comments","statuses_url":"https://api.github.com/repos/afsy/website/statuses/b1e58f01a9090bbcd55fd74c7e1fd5ad11cd2dc1","head":{"label":"stood:avent-day-11","ref":"avent-day-11","sha":"b1e58f01a9090bbcd55fd74c7e1fd5ad11cd2dc1","user":{"login":"stood","id":327248,"avatar_url":"https://avatars1.githubusercontent.com/u/327248?v=4","gravatar_id":"","url":"https://api.github.com/users/stood","html_url":"https://github.com/stood","followers_url":"https://api.github.com/users/stood/followers","following_url":"https://api.github.com/users/stood/following{/other_user}","gists_url":"https://api.github.com/users/stood/gists{/gist_id}","starred_url":"https://api.github.com/users/stood/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stood/subscriptions","organizations_url":"https://api.github.com/users/stood/orgs","repos_url":"https://api.github.com/users/stood/repos","events_url":"https://api.github.com/users/stood/events{/privacy}","received_events_url":"https://api.github.com/users/stood/received_events","type":"User","site_admin":false},"repo":{"id":113683683,"name":"website","full_name":"stood/website","owner":{"login":"stood","id":327248,"avatar_url":"https://avatars1.githubusercontent.com/u/327248?v=4","gravatar_id":"","url":"https://api.github.com/users/stood","html_url":"https://github.com/stood","followers_url":"https://api.github.com/users/stood/followers","following_url":"https://api.github.com/users/stood/following{/other_user}","gists_url":"https://api.github.com/users/stood/gists{/gist_id}","starred_url":"https://api.github.com/users/stood/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stood/subscriptions","organizations_url":"https://api.github.com/users/stood/orgs","repos_url":"https://api.github.com/users/stood/repos","events_url":"https://api.github.com/users/stood/events{/privacy}","received_events_url":"https://api.github.com/users/stood/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/stood/website","description":"👩‍💻👨‍💻 afsy.fr website ✨","fork":true,"url":"https://api.github.com/repos/stood/website","forks_url":"https://api.github.com/repos/stood/website/forks","keys_url":"https://api.github.com/repos/stood/website/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stood/website/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stood/website/teams","hooks_url":"https://api.github.com/repos/stood/website/hooks","issue_events_url":"https://api.github.com/repos/stood/website/issues/events{/number}","events_url":"https://api.github.com/repos/stood/website/events","assignees_url":"https://api.github.com/repos/stood/website/assignees{/user}","branches_url":"https://api.github.com/repos/stood/website/branches{/branch}","tags_url":"https://api.github.com/repos/stood/website/tags","blobs_url":"https://api.github.com/repos/stood/website/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stood/website/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stood/website/git/refs{/sha}","trees_url":"https://api.github.com/repos/stood/website/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stood/website/statuses/{sha}","languages_url":"https://api.github.com/repos/stood/website/languages","stargazers_url":"https://api.github.com/repos/stood/website/stargazers","contributors_url":"https://api.github.com/repos/stood/website/contributors","subscribers_url":"https://api.github.com/repos/stood/website/subscribers","subscription_url":"https://api.github.com/repos/stood/website/subscription","commits_url":"https://api.github.com/repos/stood/website/commits{/sha}","git_commits_url":"https://api.github.com/repos/stood/website/git/commits{/sha}","comments_url":"https://api.github.com/repos/stood/website/comments{/number}","issue_comment_url":"https://api.github.com/repos/stood/website/issues/comments{/number}","contents_url":"https://api.github.com/repos/stood/website/contents/{+path}","compare_url":"https://api.github.com/repos/stood/website/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stood/website/merges","archive_url":"https://api.github.com/repos/stood/website/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stood/website/downloads","issues_url":"https://api.github.com/repos/stood/website/issues{/number}","pulls_url":"https://api.github.com/repos/stood/website/pulls{/number}","milestones_url":"https://api.github.com/repos/stood/website/milestones{/number}","notifications_url":"https://api.github.com/repos/stood/website/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stood/website/labels{/name}","releases_url":"https://api.github.com/repos/stood/website/releases{/id}","deployments_url":"https://api.github.com/repos/stood/website/deployments","created_at":"2017-12-09T16:27:44Z","updated_at":"2017-12-09T16:27:46Z","pushed_at":"2017-12-10T17:08:33Z","git_url":"git://github.com/stood/website.git","ssh_url":"git@github.com:stood/website.git","clone_url":"https://github.com/stood/website.git","svn_url":"https://github.com/stood/website","homepage":"http://afsy.fr","size":22358,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"afsy:master","ref":"master","sha":"a7ac3e43c637687922ad7e106cbc1588f0a7a6da","user":{"login":"afsy","id":134074,"avatar_url":"https://avatars2.githubusercontent.com/u/134074?v=4","gravatar_id":"","url":"https://api.github.com/users/afsy","html_url":"https://github.com/afsy","followers_url":"https://api.github.com/users/afsy/followers","following_url":"https://api.github.com/users/afsy/following{/other_user}","gists_url":"https://api.github.com/users/afsy/gists{/gist_id}","starred_url":"https://api.github.com/users/afsy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/afsy/subscriptions","organizations_url":"https://api.github.com/users/afsy/orgs","repos_url":"https://api.github.com/users/afsy/repos","events_url":"https://api.github.com/users/afsy/events{/privacy}","received_events_url":"https://api.github.com/users/afsy/received_events","type":"Organization","site_admin":false},"repo":{"id":1866271,"name":"website","full_name":"afsy/website","owner":{"login":"afsy","id":134074,"avatar_url":"https://avatars2.githubusercontent.com/u/134074?v=4","gravatar_id":"","url":"https://api.github.com/users/afsy","html_url":"https://github.com/afsy","followers_url":"https://api.github.com/users/afsy/followers","following_url":"https://api.github.com/users/afsy/following{/other_user}","gists_url":"https://api.github.com/users/afsy/gists{/gist_id}","starred_url":"https://api.github.com/users/afsy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/afsy/subscriptions","organizations_url":"https://api.github.com/users/afsy/orgs","repos_url":"https://api.github.com/users/afsy/repos","events_url":"https://api.github.com/users/afsy/events{/privacy}","received_events_url":"https://api.github.com/users/afsy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/afsy/website","description":"👩‍💻👨‍💻 afsy.fr website ✨","fork":false,"url":"https://api.github.com/repos/afsy/website","forks_url":"https://api.github.com/repos/afsy/website/forks","keys_url":"https://api.github.com/repos/afsy/website/keys{/key_id}","collaborators_url":"https://api.github.com/repos/afsy/website/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/afsy/website/teams","hooks_url":"https://api.github.com/repos/afsy/website/hooks","issue_events_url":"https://api.github.com/repos/afsy/website/issues/events{/number}","events_url":"https://api.github.com/repos/afsy/website/events","assignees_url":"https://api.github.com/repos/afsy/website/assignees{/user}","branches_url":"https://api.github.com/repos/afsy/website/branches{/branch}","tags_url":"https://api.github.com/repos/afsy/website/tags","blobs_url":"https://api.github.com/repos/afsy/website/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/afsy/website/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/afsy/website/git/refs{/sha}","trees_url":"https://api.github.com/repos/afsy/website/git/trees{/sha}","statuses_url":"https://api.github.com/repos/afsy/website/statuses/{sha}","languages_url":"https://api.github.com/repos/afsy/website/languages","stargazers_url":"https://api.github.com/repos/afsy/website/stargazers","contributors_url":"https://api.github.com/repos/afsy/website/contributors","subscribers_url":"https://api.github.com/repos/afsy/website/subscribers","subscription_url":"https://api.github.com/repos/afsy/website/subscription","commits_url":"https://api.github.com/repos/afsy/website/commits{/sha}","git_commits_url":"https://api.github.com/repos/afsy/website/git/commits{/sha}","comments_url":"https://api.github.com/repos/afsy/website/comments{/number}","issue_comment_url":"https://api.github.com/repos/afsy/website/issues/comments{/number}","contents_url":"https://api.github.com/repos/afsy/website/contents/{+path}","compare_url":"https://api.github.com/repos/afsy/website/compare/{base}...{head}","merges_url":"https://api.github.com/repos/afsy/website/merges","archive_url":"https://api.github.com/repos/afsy/website/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/afsy/website/downloads","issues_url":"https://api.github.com/repos/afsy/website/issues{/number}","pulls_url":"https://api.github.com/repos/afsy/website/pulls{/number}","milestones_url":"https://api.github.com/repos/afsy/website/milestones{/number}","notifications_url":"https://api.github.com/repos/afsy/website/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/afsy/website/labels{/name}","releases_url":"https://api.github.com/repos/afsy/website/releases{/id}","deployments_url":"https://api.github.com/repos/afsy/website/deployments","created_at":"2011-06-08T16:18:29Z","updated_at":"2017-12-05T18:44:59Z","pushed_at":"2017-12-10T18:41:32Z","git_url":"git://github.com/afsy/website.git","ssh_url":"git@github.com:afsy/website.git","clone_url":"https://github.com/afsy/website.git","svn_url":"https://github.com/afsy/website","homepage":"http://afsy.fr","size":22139,"stargazers_count":23,"watchers_count":23,"language":"HTML","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":48,"mirror_url":null,"archived":false,"open_issues_count":8,"license":null,"forks":48,"open_issues":8,"watchers":23,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/afsy/website/pulls/113"},"html":{"href":"https://github.com/afsy/website/pull/113"},"issue":{"href":"https://api.github.com/repos/afsy/website/issues/113"},"comments":{"href":"https://api.github.com/repos/afsy/website/issues/113/comments"},"review_comments":{"href":"https://api.github.com/repos/afsy/website/pulls/113/comments"},"review_comment":{"href":"https://api.github.com/repos/afsy/website/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/afsy/website/pulls/113/commits"},"statuses":{"href":"https://api.github.com/repos/afsy/website/statuses/b1e58f01a9090bbcd55fd74c7e1fd5ad11cd2dc1"}},"author_association":"NONE"}}
{"actor":{"display_login":"hhamon"}}