Using Github Webhooks with PHP
In the first part of our series, we talked about the Github API and built a demo in the process. In this part we are going to explore the Webhooks API, and we will build a demo to showcase the API usage. Let’s get started.
What We’re Building
In this article we are going to set up a demo that receives every push from Github and saves it to a database. We are also going to create a page to display the list of our repository committers ordered by the number of commits. You can check the final result on Github.
Setting Up the Environment
We will be using Laravel 5 as our framework, and because our application must be accessible from the internet to work with Github Webhooks, we can use Heroku to host our code. You can check this post to get started with Heroku.
Configuration
Adding Mysql
After creating our app on Heroku, we need to add MySQL to store the received data.
heroku addons:add cleardb
You can go to your dashboard to get your database connection credentials. Set the necessary variables inside your config as you can see in the example below.
MYSQL_HOST=‘HOST’
MYSQL_DATABASE=‘DB’
MYSQL_USERNAME=‘USERNAME’
MYSQL_PASSWORD=‘PASSWORD’
Register Github Hooks
To register a new hook, navigate to a repository inside your account, then go to the settings tab and select Webhooks & Services
.
After setting your application URL endpoint to receive the payload, you can set a secret key to be sent along with the payload to verify the request origin. By default, Github will send you only push events, but you can select individual events from the list. For our demo, the push event is sufficient.
Building a Demo
Creating The Database Tables
php artisan make:migration create_hooks_table
// database/migrations/XXX_create_hooks_table.php
class CreateHooksTable extends Migration
{
public function up()
{
Schema::create('hooks', function (Blueprint $table) {
$table->increments('id');
$table->string('event_name', 100);
//you can separate the sender, repo...etc. but let's just keep it simple
$table->text('payload');
$table->timestamps();
});
}
public function down()
{
Schema::drop('hooks');
}
}
We only need a hooks
table to store the incoming requests. Registering the event_name
will help us in the future if we plan to track other events, and it will make it easier for us to look up the database.
The payload field holds the request body. You can split this field into multiple parts and create a sender, a repository, a list of commits, etc.
After running the migration using php artisan migrate
, we will create our Hook
model class.
// app/Hook.php
class Hook extends Model
{
protected $table = ‘hooks’;
}
Creating Routes
Our demo application will only have two routes.
- an
events
route, which will receive the Github event payload. - a
/report/contributions
route, which will display a chart of project contributors.
// app/Http/routes.php
Route::post(‘/events’, [‘uses’ => ‘GithubController@storeEvents’]);
// app/Http/Controllers/GithubController.php
public function storeEvents(Request $request) {
$event_name = $request->header(‘X-Github-Event’);
$body = json_encode(Input::all());
$hook = new Hook;
$hook->event_name = $event_name;
$hook->payload = $body;
$hook->save();
return ‘’;// 200 OK
}
The X-Github-Event
header value contains the event name that occurred. In this case, it will be equal to push
. If you want to test the request origin using the secret key you provided previously on Github, you can use the X-Hub-Signature
header value. You can read more in the documentation.
Github will send you an almost real time request. If you are using Heroku for deployment, you can use the logger to monitor requests while testing. You can read more about that in the documentation. You can also navigate to your Webhook configuration on Github and scroll to the Recent Deliveries
tab at the bottom.
heroku logs —tail -n 0 —ps router
To display the list of contributors I’m going to use the ChartJs JavaScript library. You can check out this getting started guide for a good intro.
// app/Http/routes.php
Route::get(‘/reports/contributions.json’, [‘uses’ => ‘GithubController@contributionsJson’]);
// app/Http/Controllers/GithubController.php
public function contributionsJson()
{
$hooks = Hook::where('event_name', '=', 'push')->get(['payload']);
$users = [];
$hooks->each(function ($item) use (&$users) {
$item = json_decode($item['payload']);
$pusherName = $item->pusher->name;
$commitsCount = count($item->commits);
$users[$pusherName] = array_pull($users, $pusherName, 0) + $commitsCount;
});
return [
'users' => array_keys($users),
'commits' => array_values($users)
];
}
The /reports/contributions.json
route will retrieve the list of push events from our database and loop through it to retrieve the list of users by commits. We then return the list of users and commits separately to suit the library needs. However, you can also choose to do it on the front-end using some Javascript.
// app/Http/routes.php
Route::get(‘/reports/contributions’, [‘uses’ => ‘GithubController@contributions’]);
// app//Http/Contrllers/GithubController.php
public function contributions()
{
return View::make('reports.contributions');
}
// resources/views/reports/contributions.blade.php
<canvas id=“contributions” width=“400” height=“400”></canvas>
<script>
var ctx = document.getElementById(“contributions”).getContext(“2d”);
$.ajax({
url: “/reports/contributions.json”,
dataType: “json”,
type: “GET”,
success: function(response){
var data = {
labels: response.users,
datasets: [
{
data: response.commits
}]
};
new Chart(ctx).Bar(data, {});
}
});
</script>
After getting the data from our JSON endpoint, we create a new Chart
object. You can read more about the available options in the documentation.
Conclusion
Here’s a real world example – if you’ve ever contributed to a Symfony project on Github, you’ll have noticed that when you send a pull request, your code is being checked by an external service called fabbot.io. This service is watching the repository for every new pull request and does some checks on your code. You can visit the website for more info, and you can also register your own repository to be checked.
The Github API has a lot to offer, and the documentation makes it really easy to get started. I do recommend you check it out. Additionally, you can check the final result on Github. And like always, if you have any questions, don’t hesitate to post them below.