Creating the Sidebar Profile Component


Now that we have a base layout and Bootstrap styles to work with, in this video we are going implement the sidebar.

In the previous video we saw how the finished Symfony application will look when rendering out to your screen. To get yourself into a mindset that will make your development easier, it's best to think about how we can split up our page into smaller chunks, that are not only easier to re-use, but also easier to maintain.

Looking at the finished project, it is clear we have two main blocks to our app - the sidebar, and the main content area. In this video we will focus on the sidebar, and in the next, sort out the main content area. You can start with either, the technique used is the same.

Ultimately we know that we want to pull in the real GitHub data from the GitHub API for whichever username we specify. Once the data is pulled down from GitHub, it should correctly populate the various placeholders inside our template to render out the sidebar (profile), or main content area (repositories list).

The thing is, there's a few concepts going on here, and thinking about them all together is not only quite overwhelming, but can be fairly confusing. Let's break down the steps to get the sidebar sorted:

  • Get the data from the GitHub API for the requested Username
  • Transform the data returned from the API into a format we can use in our template
  • Figure out what placeholders / template variables we will need
  • Create the HTML 'shell' with Bootstrap CSS classes etc
  • Pass in these placeholders from our render method inside our controller (GitHutController)

That's quite a lot of 'stuff' happening, and in the real world, there would be a few more steps in here too - handling a situation where GitHub's API was down, or returned bad data, for example.

Life is generally much easier for us as developers when, instead of having one big chunk of code that tries to do all these five things (and more), we instead break down each step / bullet point into one or more small blocks of code, that ultimately does very little. We can then think about our code like Lego, snapping together small pieces to make a castle, or a pirate ship, or the Death Star.

Getting Data From GitHub

To begin with, we want to think about the data that GitHub is going to return to us when we send in a request to the /users/:username GitHub API endpoint. We can make this request simply by browsing to the endpoint in a web browser, e.g. https://api.github.com/users/codereviewvideos returns:

{
  "login": "codereviewvideos",
  "id": 12968163,
  "avatar_url": "https://avatars.githubusercontent.com/u/12968163?v=3",
  "gravatar_id": "",
  "url": "https://api.github.com/users/codereviewvideos",
  "html_url": "https://github.com/codereviewvideos",
  "followers_url": "https://api.github.com/users/codereviewvideos/followers",
  "following_url": "https://api.github.com/users/codereviewvideos/following{/other_user}",
  "gists_url": "https://api.github.com/users/codereviewvideos/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/codereviewvideos/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/codereviewvideos/subscriptions",
  "organizations_url": "https://api.github.com/users/codereviewvideos/orgs",
  "repos_url": "https://api.github.com/users/codereviewvideos/repos",
  "events_url": "https://api.github.com/users/codereviewvideos/events{/privacy}",
  "received_events_url": "https://api.github.com/users/codereviewvideos/received_events",
  "type": "User",
  "site_admin": false,
  "name": "Code Review Videos",
  "company": "Code Review Videos",
  "blog": "https://codereviewvideos.com/",
  "location": "Preston, Lancs, UK",
  "email": null,
  "hireable": null,
  "bio": null,
  "public_repos": 11,
  "public_gists": 0,
  "followers": 0,
  "following": 0,
  "created_at": "2015-06-19T14:39:34Z",
  "updated_at": "2016-02-20T13:08:46Z"
}

Behold my amazing stats.

This data is returned as JSON (JavaScript Object Notation). This is largely how most modern API's will return data to you. This is good, as PHP is easily able to work with JSON (json_decode, and json_encode), as we shall see very shortly.

But for now, let's not concern ourselves with how we will really get this data into our application. Instead, let's just manually do the job. After all, to begin with it is easiest to do a task manually, until such time as it becomes more efficient to automate it.

We are able to convert this JSON data to a PHP array using json_decode:

$data = json_decode('{"login": "codereviewvideos"}', true);

print_r($data);

// Array ( [login] => codereviewvideos )

There's a full manual page on PHP.net about json_decode so be sure to read up there if you are unsure, or simply want to know more.

All we need to know here is that we pass in a JSON string, and the second parameter of true, which tells PHP to convert our JSON string into a PHP array.

This is good, as the render function we've already covered, and Twig in general, really likes Arrays.

Transform The GitHub Data to Meet Our Requirements

GitHub sends back the data in one big list. This is great, but it doesn't really teach us a great deal. So let's make things a little more interesting, and as it happens, a little bit more like what you would likely use in a real world Symfony application.

Our sidebar will contain a picture, our username and login name, and then a couple of lists of data.

We could hardcode those lists, but let's use this as an opportunity to learn Twig's for ... in syntax.

We'll see more on this when we create our template, but to use a for loop we have to have some data to loop through.

Well, we already know that the json_decode is going to have converted the GitHub JSON in to a big PHP array for us.

Let's pick through this mass of data, and extract the bits we are care about for our sidebar:

$templateData = [
    'avatar_url'  => $data['avatar_url'],
    'name'        => $data['name'],
    'login'       => $data['login'],
    'details'     => [
        'company'   => $data['company'],
        'location'  => $data['location'],
        'joined_on' => 'Joined on Fake Date For Now',
    ],
    'blog'        => $data['blog'],
    'social_data' => [
        'followers'    => $data['followers'],
        'following'    => $data['following'],
        'public_repos' => $data['public_repos'],
    ]
];

Each of the $data fields matches up exactly to a field from the GitHub JSON.

What I am then doing here is mapping the GitHub data ($data) on to field names that make sense to me and my app.

For example, the details key contains a sub array, containing some related parts from the GitHub results. And likewise for social_data.

These array keys can be anything, but I have kept them roughly in-line with GitHub's existing field names, just for ease of use.

However, currently we aren't actually getting the real data from GitHub. So, let's just hardcode the data for the moment:

$templateData = [
    'avatar_url'  => 'https://avatars.githubusercontent.com/u/12968163?v=3',
    'name'        => 'Code Review Videos',
    'login'       => 'codereviewvideos',
    'details'     => [
        'company'   => 'Code Review Videos',
        'location'  => 'Preston, Lancs, UK',
        'joined_on' => 'Joined on Fake Date For Now',
    ],
    'blog'        => 'https://codereviewvideos.com/',
    'social_data' => [
        'followers'    => 11,
        'following'    => 22,
        'public_repos' => 33,
    ]
];

Note, this is the PHP 5.4 array short hand syntax, equal to $templateData = array();.

This is good enough to populate the sidebar.

Sidebar Variables

The next thing we need to know is: what variables are going to be available to us in our Twig template?

In the previous video we created our first Controller action:

    /**
     * @Route("/", name="githut")
     */
    public function githutAction(Request $request)
    {
        return $this->render('githut/index.html.twig');
    }

And I briefly showed how the render method allows data to be passed in as the second property (after the template name / path), as an array.

An array! What a stroke of luck. It's almost like this was all planned.

So, we can go ahead and pass in our hardcoded data like so:

    /**
     * @Route("/", name="githut")
     */
    public function githutAction(Request $request)
    {
        $templateData = [
            'avatar_url'  => 'https://avatars.githubusercontent.com/u/12968163?v=3',
            'name'        => 'Code Review Videos',
            'login'       => 'codereviewvideos',
            'details'     => [
                'company'   => 'Code Review Videos',
                'location'  => 'Preston, Lancs, UK',
                'joined_on' => 'Joined on Fake Date For Now',
            ],
            'blog'        => 'https://codereviewvideos.com/',
            'social_data' => [
                'followers'    => 11,
                'following'    => 22,
                'public_repos' => 33,
            ]
        ];

        return $this->render('githut/index.html.twig', templateData);
    }

Infact, you can get even snazzier, and simply inline the array:

    /**
     * @Route("/", name="githut")
     */
    public function githutAction(Request $request)
    {
        return $this->render('githut/index.html.twig', [
            'avatar_url'  => 'https://avatars.githubusercontent.com/u/12968163?v=3',
            'name'        => 'Code Review Videos',
            'login'       => 'codereviewvideos',
            'details'     => [
                'company'   => 'Code Review Videos',
                'location'  => 'Preston, Lancs, UK',
                'joined_on' => 'Joined on Fake Date For Now',
            ],
            'blog'        => 'https://codereviewvideos.com/',
            'social_data' => [
                'followers'    => 11,
                'following'    => 22,
                'public_repos' => 33,
            ]
        ]);
    }

Ok cool. So to answer the earlier question: what variables are available on our template?

The answer is - any key we just passed in. Let's take a look at the template now to see this in action.

The Twig Template

The HTML shell for the profile is being kept as bare bones as possible. I'm predominantly using my own class names, but am making use of Bootstrap for list styling.

The nice part about this is that this template could then be re-used in more than just the sidebar, if the need arises.

<!-- app/Resources/views/githut/index.html.twig -->

{% extends 'base.html.twig' %}

{% block body %}
    <div class="col-sm-3">

        <img src="{{ avatar_url }}" class="thumbnail img-responsive" />

        <h1 class="profile-name">{{ name }}</h1>
        <h2 class="account-name">{{ login }}</h2>

        <hr />

        <ul class="list-unstyled">
            {% for detail in details %}
                <li>{{ detail }}</li>
            {% endfor %}
            <li><a href="{{ blog }}">{{ blog }}</a></li>
        </ul>

        <hr />

        <ul class="list-inline">
            {% for key, value in social_data %}
                <li><span class="social-metric-count">{{ value }}</span>
                    <br/>
                    <span class="small muted">{{ key }}</span>
                </li>
            {% endfor %}
        </ul>

    </div>

    <div class="col-sm-9">
        {{ render (controller('AppBundle:Githut:repos', { 'username': username })) }}
    </div>
{% endblock %}

As you can see, each of the array keys we created earlier are available for use on the template.

We need to wrap any code / variables / logic inside {{ }} tags, but honestly, I find Twig super easy to both use and read. This template syntax is incredibly common, from Python (Jinja) to NodeJS (Swig) and beyond. Learn once, use many other places.

Twig also has a brilliant and easy to use reference section (scroll down a touch) so be sure to check that out.

Extracting The Sidebar

This is great, and it should work just fine. But our sidebar content is now heavily tied to our index.html.twig file. And this just won't do.

We can easily extract just the sidebar markup into its own template, which we shall called profile.html.twig. So let's quickly do that now:

<!-- app/Resources/views/githut/profile.html.twig -->

<img src="{{ avatar_url }}" class="thumbnail img-responsive" />

<h1 class="profile-name">{{ name }}</h1>
<h2 class="account-name">{{ login }}</h2>

<hr />

<ul class="list-unstyled">
    {% for detail in details %}
        <li>{{ detail }}</li>
    {% endfor %}
    <li><a href="{{ blog }}">{{ blog }}</a></li>
</ul>

<hr />

<ul class="list-inline">
    {% for key, value in social_data %}
        <li><span class="social-metric-count">{{ value }}</span>
            <br/>
            <span class="small muted">{{ key }}</span>
        </li>
    {% endfor %}
</ul>

And then let's update the index.html.twig file to simply include this template:

<!-- app/Resources/views/githut/index.html.twig -->

{% extends 'base.html.twig' %}

{% block body %}
    <div class="col-sm-3">

        {% include ':githut:profile.html.twig' %}

    </div>

    <div class="col-sm-9">
        Repos to go here in next video
    </div>
{% endblock %}

And that should be it for the moment.

We now have a re-usable profile component that lives in our sidebar, but could be re-used inside a dedicated profile page or similar.

This modularisation keeps our blocks of code smaller, easier to maintain, and hopefully, easier to spot and fix bugs.

We're going to do a very similar process for our Repositories content in the very next video.

Code For This Course

Get the code for this course.

Episodes