Making Dependable #1
By: Christopher Pitt
Tags: Github, Laravel, PHP

I work at SilverStripe. Aside from the framework and CMS modules, we have many smaller modules to maintain. And recently it’s been difficult to demo bug fixes or new features, from multiple contributors/branches, in the same installation.

One of the ideas we’ve had is to create an application; which can build a custom Composer dependency map, and deploy that to a new server. It would also be great if there was an interface for deciding which dependencies are included, and a unique URL for each installation.

That’s what I want to build.

Creating A New Project

I’ve chosen to make this project in Laravel 5.x, for a couple of reasons:

I want to learn more about Laravel 5.x. I’m particularly interested the new features of 5.1. In addition to my work at SilverStripe, I also help run the Wellington Laravel meet-up group. This is something I’d like to share with them, and perhaps collaborate on as a group.

If you haven’t installed Composer, globally, then do that. You can find out how at https://getcomposer.org. Life’s too short to have to manage a composer.phar in every project…

So I’ve installed Laravel 5.x, via Composer:

$ composer create-project laravel/laravel dependable

Time to get some coffee…

Creating The Database

To begin with, I want to store developer details. Everybody that uses this (or whose repositories and branches are folded into new installations) need an account. It doesn’t have to have all of their information, but there needs to be a way for developers to connect with Github, and optionally set a password.

Connecting with Github seems to be a job for Socialite. I want to see what fields Github will give me before I create the database tables for the rest of the information…

$ composer require laravel/socialite

Have to remember to add the Socialite service provider and facade! The facade isn’t strictly necessary, but it makes the example code cleaner and is just as easy to write tests for.

This happens in ~/config/app.php

I also need to create application keys. You can create new application keys through https://github.com/settings/developers. For the URL I just set http://dependable.assertchris.io (even though that doesn’t exist yet). For the callback URL I set http://dependable.assertchris.io/connect/github.

I should add these keys to config:

"github" => [

"client_id" => env("GITHUB_KEY"),

"client_secret" => env("GITHUB_SECRET"),

"redirect" => env("GITHUB_REDIRECT"),

],

This happens in ~/config/services.php

It’s going to be a pain to develop on a live URL, so I’ll fake it by adding a hosts entry:

127.0.0.1 dependable.assertchris.io

This happens in /etc/hosts

I also need to point my local web server to the public folder:

<VirtualHost *:80>

<Directory "/path/to/dependable/public">

AllowOverride All

Options Indexes MultiViews FollowSymLinks

Require all granted

</Directory>



DocumentRoot /path/to/dependable/public

ServerName dependable.assertchris.io

</VirtualHost>

This happens in /etc/apache2/extra/httpd-vhosts.conf

$ apachectl restart

This makes the site viewable, in a browser. Let’s add a couple of routes, to connect to a Github account:

use Illuminate\Support\Facades\Route;

use Laravel\Socialite\Facades\Socialite;



Route::get("/", "WelcomeController@index");



Route::get("redirect/github", function () {

return Socialite::with("github")->redirect();

});



Route::get("connect/github", function () {

$data = Socialite::with("github")->user();



// save data...

});

This happens in ~/app/Http/routes.php

Amazingly, this is all the code I need to get my Github profile details. The results resemble the following:

Laravel\Socialite\Two\User Object

(

[token] => 057e0a...

[id] => 200609...

[nickname] => assertchris

[name] => Christopher Pitt

[email] => cgpitt@gmail.com

[avatar] => https://avatars.githubusercontent.com...

[user] => Array

(

[login] => assertchris

...

)

)

Looks like I can get email, name and avatar. I can also list the repositories, which will be useful later. I’ll still need a password field, but I’m going to ignore it for now.

So I’ll need a table to store:

ID Name Nickname Email Avatar

I can add more to that later! I’ll generate an empty migration, and add the fields we need, after:

$ php artisan make:migration developer

I’ll add the fields I need:

use Illuminate\Database\Migrations\Migration;

use Illuminate\Database\Schema\Blueprint;

use Illuminate\Support\Facades\Schema;



class Developer extends Migration

{

/**

* Run the migrations.

*/

public function up()

{

Schema::create("developer", function (Blueprint $table) {

$table->increments("id");

$table->string("github_id");

$table->string("github_name");

$table->string("github_nickname");

$table->string("github_email");

$table->string("github_avatar");

$table->timestamps();

});

}



/**

* Reverse the migrations.

*/

public function down()

{

Schema::dropIfExists("developer");

}

}

This happens in ~/database/migrations/

2015_05_26_000000_create_developer_table.php

I’ve also changed my default database driver to sqlite, and run the migrations with:

$ touch storage/database.sqlite

$ php artisan migrate

I’ll update the landing page, to show which developers are connected:

namespace App\Models;



use Illuminate\Database\Eloquent\Model;



class Developer extends Model

{

/**

* @var string

*/

protected $table = "developer";



/**

* @var array

*/

protected $fillable = [

"github_id",

"github_nickname",

"github_name",

"github_email",

"github_avatar",

];

}

This happens in ~/app/Models/Developer.php

use App\Models\Developer;



Route::get("connect/github", function () {

$data = Socialite::with("github")->user();



$developer = Developer::where("github_id", $data->id)

->first();



if (!$developer) {

Developer::create([

"github_id" => $data->id,

"github_nickname" => $data->nickname,

"github_name" => $data->name,

"github_email" => $data->email,

"github_avatar" => $data->avatar

]);

}



return redirect("/");

});

This happens in ~/app/Http/routes.php

use App\Models\Developer;



Route::get("/", function () {

$developers = Developer::all();



return view("index", [

"developers" => $developers

]);

});

This happens in ~/app/Http/routes.php

<h1>Developers</h1>

<ol>

@foreach($developers as $developer)

<li>{{ $developer->github_name }}</li>

@endforeach

</ol>

<a href="{{ url("redirect/github") }}">Connect to Github</a>

This happens in ~/resources/views/index.blade.php

This list is rough, but it demonstrates how I can connect (with Github) and store knowledge of that connection in the database. The next step will be to gather repository data, so the application can start to create custom composer.json dependency maps…

Notes

Laravel ships with some scaffolding. It’s mostly to do with user management and authentication. I removed it with:

$ php artisan fresh

I also had to remove the User model, and change a few routes.

You can find the code at https://github.com/assertchris/dependable.