-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
310 lines (229 loc) · 28.3 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Bram Cordie</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="This is the blog of Bram Cordie">
<meta property="og:type" content="website">
<meta property="og:title" content="Bram Cordie">
<meta property="og:url" content="http://bramcordie.github.io/">
<meta property="og:site_name" content="Bram Cordie">
<meta property="og:description" content="This is the blog of Bram Cordie">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Bram Cordie">
<meta name="twitter:description" content="This is the blog of Bram Cordie">
<link rel="alternative" href="/atom.xml" title="Bram Cordie" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/css/style.css" type="text/css">
</head>
<body>
<div id="container">
<div id="wrap">
<header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer">
<div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="/" id="logo">Bram Cordie</a>
</h1>
<h2 id="subtitle-wrap">
<a href="/" id="subtitle">blog</a>
</h2>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"></a>
<a class="main-nav-link" href="/">Home</a>
<a class="main-nav-link" href="/archives">Archives</a>
</nav>
<nav id="sub-nav">
<a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
<a id="nav-search-btn" class="nav-icon" title="Search"></a>
</nav>
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" results="0" class="search-form-input" placeholder="Search"><input type="submit" value="" class="search-form-submit"><input type="hidden" name="q" value="site:http://bramcordie.github.io"></form>
</div>
</div>
</div>
</header>
<div class="outer">
<section id="main">
<article id="post-Reviewing-my-first-Symfony2-project" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2014/09/21/Reviewing-my-first-Symfony2-project/" class="article-date">
<time datetime="2014-09-21T12:42:15.000Z" itemprop="datePublished">Sep 21 2014</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2014/09/21/Reviewing-my-first-Symfony2-project/">Reviewing my first Symfony2 project</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="History">History</h2>
<p>About 2 years ago, I built my first Symfony2 project. The most recent version
back then was 2.0.12 which I figured out from the now deprecated <em>/deps</em> file.</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">[symfony]</div><div class="line"> <span class="variable">git=</span>http://github.com/symfony/symfony.git</div><div class="line"> <span class="variable">version=</span>v2.<span class="number">0.12</span></div></pre></td></tr></table></figure>
<p>This project was part of my internship where I was given a LAMP stack to deploy
on. I chose Symfony because of the resemblance with <strong>ASP.NET MVC</strong> which I used
on a previous project and enjoyed working with.</p>
<h2 id="Deployment">Deployment</h2>
<p>I used SVN before but I wasn’t familiar with GIT. The project was under version
control but the only version I found is the zip file I handed over at the
end of my internship.</p>
<p>If this project requires further development, it would be a good idea to put
it under version control. The vendor folder can be excluded together with
the nbproject that somehow snuck into the zip file. Because <em>2.0</em> lacks
composer, the command to install vendors is different from what I’m used to
but the workflow stays the same: <code>php bin/vendors install</code></p>
<p>I had to download the missing <em>.gitignore</em> file from the symfony-standard
repository. After running <code>git init; git add .</code> in the root of the project,
further changes can be tracked.</p>
<p>After changing the <em>/app/config/parameters.ini</em> file, creating the database
and running <code>php app/console doctrine:schema:update --force</code>, the project is
available in my local development environment. The Apache and PHP config that I
use for more recent projects seems to do the job, no tweaking required. Just to
be sure, I checked <em>/web/config.php</em>, it did not complain.</p>
<h2 id="The_Application">The Application</h2>
<h3 id="Missing_data">Missing data</h3>
<p>Clicking through the website I ran into the following errors:</p>
<ul>
<li>There are no questions!</li>
<li>Can’t find any question catergories!</li>
</ul>
<p>Other pages just show a blank area where a list of options should appear. For
example: when starting an assessment you should be able to pick an <em>opleiding</em>
but none are presented.</p>
<p>There are 2 measures we can take to counter these issues.</p>
<p>First of all, showing sensible messages when data is missing to properly render
views. Depending on the role of the user and their ability to add content, you
can point them in the right direction and guide them through their first
encounter with the application.</p>
<p>A second solution is seeding the project with the help of Doctrine fixtures.
These fixtures can be run on command so they are extremely helpful if you mess
up your data or you quickly need the dummy content.</p>
<h3 id="Bulky_controllers">Bulky controllers</h3>
<p>The controllers in this project ended up being quite bulky. Looking back, they
provide plenty of examples where improvements can be made.</p>
<h4 id="Form_classes">Form classes</h4>
<p>Instead of defining forms inside the controller they can be defined in separate
form classes that can directly map to existing model entities or view models.
This way we can consolidate duplicate code in controllers and compose more
elaborate forms by combining different form classes.</p>
<h4 id="Repositories">Repositories</h4>
<p>Another way of keeping controllers dry is creating custom repository classes to
persist and retrieve entities.</p>
<p>The following controller action code looks for all assessments created between
a begin and end date.</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="preprocessor"><?php</span></div><div class="line"><span class="comment">// src/OAT/OATBundle/Controller/StatisticsController.php</span></div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">assessmentAction</span><span class="params">()</span></span></div><div class="line">{</div><div class="line"> ...</div><div class="line"> <span class="variable">$categoryAssessments</span> = <span class="variable">$this</span><span class="variable">->getDoctrine</span>()</div><div class="line"> <span class="variable">->getRepository</span>(<span class="string">'OATBundle:CategoryAssessment'</span>)</div><div class="line"> <span class="variable">->createQueryBuilder</span>(<span class="string">'c'</span>)</div><div class="line"> <span class="variable">->where</span>(<span class="string">"c.status = 1"</span>)</div><div class="line"> <span class="variable">->andWhere</span>(<span class="string">"c.created <= :endDate"</span>)</div><div class="line"> <span class="variable">->andWhere</span>(<span class="string">"c.created >= :startDate"</span>)</div><div class="line"> <span class="variable">->setParameters</span>(<span class="keyword">array</span>(<span class="string">'startDate'</span> => <span class="variable">$startDate</span>, <span class="string">'endDate'</span> => <span class="variable">$endDate</span>))</div><div class="line"> <span class="variable">->getQuery</span>()</div><div class="line"> <span class="variable">->getResult</span>();</div><div class="line"> ...</div><div class="line">}</div></pre></td></tr></table></figure>
<p>This Query can be moved to a repository class where we define it as a function.</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="preprocessor"><?php</span></div><div class="line"><span class="comment">// src/OAT/OATBundle/Entity/AssessmentRepository.php</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">AssessmentRepository</span> <span class="keyword">extends</span> <span class="title">EntityRepository</span></span></div><div class="line">{</div><div class="line"> <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">findAllByTimespan</span><span class="params">(<span class="variable">$startDate</span>, <span class="variable">$endDate</span>)</span></span>{</div><div class="line"> ...</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>We can then replace the query in the controller action with the repository
function.</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="preprocessor"><?php</span></div><div class="line"><span class="comment">// src/OAT/OATBundle/Controller/StatisticsController.php</span></div><div class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">assessmentAction</span><span class="params">()</span></span></div><div class="line">{</div><div class="line"> ...</div><div class="line"> <span class="variable">$categoryAssessments</span> = <span class="variable">$this</span><span class="variable">->getDoctrine</span>()</div><div class="line"> <span class="variable">->getRepository</span>(<span class="string">'OATBundle:CategoryAssessment'</span>)</div><div class="line"> <span class="variable">->findAllByTimespan</span>(<span class="variable">$startDate</span>, <span class="variable">$endDate</span>);</div><div class="line"> ...</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="Keep_domain_logic_inside_the_model">Keep domain logic inside the model</h4>
<p>The following controller action code takes a list of category groups, which
can contain overlapping categories, matches them against a list of all the
existing categories and builds a set with unique categories to create an
assessment.</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="preprocessor"><?php</span></div><div class="line"><span class="comment">// src/OAT/OATBundle/Controller/AssessmentController.php newAction</span></div><div class="line"><span class="variable">$categories</span> = <span class="keyword">Array</span>();</div><div class="line"></div><div class="line"><span class="variable">$categoryGroups</span> = <span class="variable">$this</span><span class="variable">->getDoctrine</span>()</div><div class="line"> <span class="variable">->getRepository</span>(<span class="string">'OATBundle:QuestionCategoryGroup'</span>)</div><div class="line"> <span class="variable">->findAll</span>();</div><div class="line"></div><div class="line"><span class="variable">$selectedGroups</span> = <span class="keyword">Array</span>();</div><div class="line"></div><div class="line"><span class="keyword">foreach</span>(<span class="variable">$categoryGroups</span> <span class="keyword">as</span> <span class="variable">$categoryGroup</span>)</div><div class="line">{</div><div class="line"> <span class="keyword">if</span>(<span class="variable">$request</span><span class="variable">->request</span><span class="variable">->get</span>(<span class="string">'category-group-'</span>.<span class="variable">$categoryGroup</span><span class="variable">->getId</span>())) {</div><div class="line"> <span class="variable">$selectedGroups</span>[] = <span class="variable">$categoryGroup</span>;</div><div class="line"> <span class="keyword">foreach</span>(<span class="variable">$categoryGroup</span><span class="variable">->getQuestionCategoryGroupMember</span>() <span class="keyword">as</span> <span class="variable">$category</span>)</div><div class="line"> {</div><div class="line"> <span class="keyword">if</span>(!in_array(<span class="variable">$category</span><span class="variable">->getQuestionCategory</span>(), <span class="variable">$categories</span>)) {</div><div class="line"> <span class="variable">$categories</span>[] = <span class="variable">$category</span><span class="variable">->getQuestionCategory</span>();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>You don’t want the repository call and http request logic to end up in the model.
The following code will match the requested group IDs against existing
category groups.</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="preprocessor"><?php</span></div><div class="line"><span class="comment">// src/OAT/OATBundle/Controller/AssessmentController.php</span></div><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">getRequestedCategoryGroupIds</span><span class="params">(Request <span class="variable">$request</span>)</span> </span>{</div><div class="line"> <span class="comment">// Get all the posted variables</span></div><div class="line"> <span class="variable">$postedVariables</span> = <span class="variable">$request</span><span class="variable">->request</span><span class="variable">->all</span>();</div><div class="line"></div><div class="line"> <span class="comment">// There's a post variable prefixed with 'category-group-' for each category group</span></div><div class="line"> <span class="comment">// You can't filter on key so the array must be flipped.</span></div><div class="line"> <span class="variable">$postedCategoryGroups</span> = array_filter(array_flip(<span class="variable">$postedVariables</span>), <span class="function"><span class="keyword">function</span><span class="params">(<span class="variable">$key</span>)</span> </span>{</div><div class="line"> <span class="keyword">return</span> strpos(<span class="variable">$key</span>, <span class="string">'category-group-'</span>) === <span class="number">0</span>;</div><div class="line"> });</div><div class="line"></div><div class="line"> <span class="comment">// Strip the group IDs</span></div><div class="line"> <span class="variable">$categoryGroupIds</span> = array_map(<span class="function"><span class="keyword">function</span><span class="params">(<span class="variable">$value</span>)</span> </span>{</div><div class="line"> <span class="keyword">return</span> intval(str_replace(<span class="string">"category-group-"</span>, <span class="string">""</span>, <span class="variable">$value</span>));</div><div class="line"> }, <span class="variable">$postedCategoryGroups</span>);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> <span class="variable">$categoryGroupIds</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">newAction</span><span class="params">(Request <span class="variable">$request</span>)</span> </span>{</div><div class="line"> <span class="variable">$categoryGroupIds</span> = <span class="variable">$this</span><span class="variable">->getRequestedCategoryGroupIds</span>(<span class="variable">$request</span>);</div><div class="line"></div><div class="line"> <span class="comment">// Find the matching group entities</span></div><div class="line"> <span class="variable">$categoryGroups</span> = <span class="variable">$this</span><span class="variable">->getDoctrine</span>()</div><div class="line"> <span class="variable">->getRepository</span>(<span class="string">'OATBundle:QuestionCategoryGroup'</span>)</div><div class="line"> <span class="variable">->findById</span>(<span class="variable">$categoryGroupIds</span>);</div><div class="line"> ...</div><div class="line">}</div></pre></td></tr></table></figure>
<p>Further down in the action, the category groups are added after the assessment
object is created. Instead of just passing the groups to the constructor, all
their categories were reduced to a set to use as parameter.</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="preprocessor"><?php</span></div><div class="line"><span class="comment">// src/OAT/OATBundle/Controller/AssessmentController.php</span></div><div class="line"><span class="variable">$assessment</span> = <span class="keyword">new</span> Assessment(<span class="variable">$categories</span>);</div><div class="line"><span class="variable">$assessment</span><span class="variable">->setCreated</span>(<span class="keyword">new</span> DateTime(<span class="string">"now"</span>));</div><div class="line"><span class="keyword">foreach</span>(<span class="variable">$selectedGroups</span> <span class="keyword">as</span> <span class="variable">$group</span>)</div><div class="line">{</div><div class="line"> <span class="variable">$assessment</span><span class="variable">->addQuestionCategoryGroup</span>(<span class="variable">$group</span>);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>You should be able to create an assessment by passing the groups and let
the constructor deal with them. The model should decide if duplicate categories
are allowed without involving the controller for any domain logic.</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="preprocessor"><?php</span></div><div class="line"><span class="comment">// src/OAT/OATBundle/Entity/Assessment.php</span></div><div class="line"><span class="comment">/**</span></div><div class="line"> *<span class="phpdoc"> @param</span> QuestionCategoryGroup[] $groups</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">(Array <span class="variable">$groups</span>)</span></span></div><div class="line">{</div><div class="line"> ...</div><div class="line"> <span class="keyword">foreach</span> (<span class="variable">$groups</span> <span class="keyword">as</span> <span class="variable">$group</span>) {</div><div class="line"> <span class="variable">$this</span><span class="variable">->addQuestionCategoryGroup</span>(<span class="variable">$group</span>);</div><div class="line"> }</div><div class="line"> ...</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">addQuestionCategoryGroup</span><span class="params">(QuestionCategoryGroup <span class="variable">$group</span>)</span></span></div><div class="line">{</div><div class="line"> <span class="variable">$this</span><span class="variable">->questionCategoryGroups</span>[] = <span class="variable">$group</span>;</div><div class="line"></div><div class="line"> <span class="keyword">foreach</span>(<span class="variable">$group</span><span class="variable">->getQuestionCategoryGroupMembers</span>() <span class="keyword">as</span> <span class="variable">$categoryGroupMember</span>) {</div><div class="line"> <span class="variable">$this</span><span class="variable">->addQuestionCategory</span>(<span class="variable">$categoryGroupMember</span><span class="variable">->getQuestionCategory</span>());</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">addQuestionCategory</span><span class="params">(QuestionCategory <span class="variable">$category</span>)</span></span></div><div class="line">{</div><div class="line"> <span class="keyword">if</span>(!in_array(<span class="variable">$this</span><span class="variable">->getQuestionCategories</span>(), <span class="variable">$category</span>) {</div><div class="line"> <span class="variable">$this</span><span class="variable">->questionCategories</span>[] = <span class="variable">$category</span>;</div><div class="line"> };</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="Managing_Dependencies">Managing Dependencies</h3>
<h4 id="Back-end">Back-end</h4>
<p>Switching to <strong>Composer</strong> would make it easier to manage security updates. This
requires updating Symfony to at least <em>v2.1</em>. Going for <em>v2.3</em> will get you
long-term support and makes sure you are only running stable versions of external
dependencies. This project does not rely heavily on third-party bundles so upgrading
should not be that hard by following the <a href="https://github.com/symfony/symfony/blob/master/UPGRADE-2.1.md" target="_blank" rel="external">Symfony upgrade guide</a>.</p>
<h4 id="Front-end">Front-end</h4>
<p>Javascript libraries are stored in the OAT bundle. This creates unneeded bloat
in the repository and requires you to downloaded and add them manually.</p>
<p> For more recent projects I’m using <a href="http://bower.io/" target="_blank" rel="external">Bower</a> and it makes
developing with small javascript libraries so much easier. You can copy over a
list of dependencies from existing projects and running <code>bower install</code> will
get the locally cached files or download them when needed. Assetic can still be
used to minify everything to a single file.</p>
</div>
<footer class="article-footer">
<a data-url="http://bramcordie.github.io/2014/09/21/Reviewing-my-first-Symfony2-project/" data-id="1lia0j5l52m1voze" class="article-share-link">Share</a>
<a href="http://bramcordie.github.io/2014/09/21/Reviewing-my-first-Symfony2-project/#disqus_thread" class="article-comment-link">Comments</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Symfony2/">Symfony2</a></li></ul>
</footer>
</div>
</article>
</section>
<aside id="sidebar">
<div class="widget-wrap">
<h3 class="widget-title">Tags</h3>
<div class="widget">
<ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/Symfony2/">Symfony2</a><span class="tag-list-count">1</span></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Tag Cloud</h3>
<div class="widget tagcloud">
<a href="/tags/Symfony2/" style="font-size: NaNpx;">Symfony2</a>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Archives</h3>
<div class="widget">
<ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2014/09/">September 2014</a><span class="archive-list-count">1</span></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Recents</h3>
<div class="widget">
<ul>
<li>
<a href="/2014/09/21/Reviewing-my-first-Symfony2-project/">Reviewing my first Symfony2 project</a>
</li>
</ul>
</div>
</div>
</aside>
</div>
<footer id="footer">
<div class="outer">
<div id="footer-info" class="inner">
© 2014 Bram Cordie<br>
Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
</div>
</div>
</footer>
</div>
<nav id="mobile-nav">
<a href="/" class="mobile-nav-link">Home</a>
<a href="/archives" class="mobile-nav-link">Archives</a>
</nav>
<script>
var disqus_shortname = 'bramcordie';
(function(){
var dsq = document.createElement('script');
dsq.type = 'text/javascript';
dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/count.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<link rel="stylesheet" href="/fancybox/jquery.fancybox.css" type="text/css">
<script src="/fancybox/jquery.fancybox.pack.js" type="text/javascript"></script>
<script src="/js/script.js" type="text/javascript"></script>
</div>
</body>
</html>