We're going to implement a simple base structure for a web blog, using Laravel as the backend part and AngularJS as the frontend.
The main goal of this article is to setup a basic communication structure between a frontend and a backend framework, with everything in it's place.
About Laravel
Laravel is awesome. There, I said it. I tend not to get over excited with most of the backend tinkering work I do, probably because I spent a few years dealing with some dark magic, but Laravel is refreshingly simple and straight to the point. Well done Sires.
Laravel will be used basically as an API, a database communication and data integrity layer. It will mainly map an "url" to a controller/action to get or save some data.
About AngularJS
AngularJS (big surprise) is awesome. However, it has a steeper learning curve in comparison with Laravel. This front end layer will turn the JSON data received from the back end and show it accordingly in the browser window. It will also be the one responsible for turning this blog into a SPA (single page application), which means it’s going to take care of all the routing and template rendering (not the Laravel ones).
The line in the sand
I am drawing one of these to make clear who's responsible for what, and hopefully prevent any further discussions with myself down to road.
I’m having a back-end layer solely dedicated into getting the correct, parse and structured data; and a front-end layer dedicated into making that shine. If any work needs to be done on any request's result, it should always be done on the Laravel side. I don't think AngularJS should be the one responsible for parsing, sorting or checking any data before showing it to the user.
In sum, Laravel’s controllers will take care of the fetching & saving of the data; AngularJS's controllers will make requests onto some classic endpoints & deal with the data that comes after those, and do it’s thing.
Setting up Laravel with Angularjs
Building an Angularjs app on top of a Laravel structure basically means it's going to be made to fit said structure. For this reason, getting Laravel up and running is my first priority. The "only" actual requirement for it is to have a local server and database up and running.
Setup Laravel
The installation process has few steps and it's really straightforward. Following Laravel's official Installation guide is enough to get things going. Here is the short version, assuming you are on the root of wherever you want to install Laravel, to me it's ../Sites/htdocs/
The mcrypt extension is required to run Laravel, so, before getting things going, install it through brew:
$ brew install php5*-mcrypt
Make sure to install the correct mcrypt version, according to your php version. If you don't have brew installed on your mac, get serious!
Also, like mcrypt, make sure that your apache configuration has mod_rewrite
enabled. Otherwise Laravel won't be allowed to take care of your routing.
Open /etc/apache2/httpd.conf
and look for mod_rewrite
. If it's commented out, uncomment it.
Next, get composer (assuming curl is installed)
$ curl -sS https://getcomposer.org/installer | php
Get Laravel
$ composer global require “laravel/installer=~1.1"
Make sure ~/.composer/vendor/bin is added to PATH
$ sudo nano /etc/paths
If it's not, add the path on the bottom of the file. Create your project (this article is specific for laravel 4.* version)
$ composer create-project laravel/laravel {project-name} 4.2 --prefer-dist
Set the correct permissions to app/storage
$ sudo chmod -R g+w <yourprojectname>/app/storage
Configure Laravel & MySQL
First, and since this is a development install, let's make it so. Turn on the debug functionality on app/config/app.php
'debug' => true,
Then, add a custom environment. This is not mandatory “per se”, however it's recommended and it will keep you from getting some warnings along the way. The default environment is the production, so, just add another one and "point it" to you local distribution in bootstrap/start.php
$env = $app->detectEnvironment(array(
'development' => array('theblog.local', ''),
'local' => array('homestead'),
));
I'm choosing to hook Laravel with a good'ol mysql database. The configuration is basically just opening /app/config/database.php
, and editing the 'connections' array on the array corresponding to 'mysql' with your mysql settings.
...
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => '<mydatabase>',
'username' => '<myusername>',
'password' => '<mypassword>',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
...
I always create a specific user for the database, and give him all the permissions (except grant) to the project table. That's not really mandatory, I just think it's a good practice. Doing this through phpmyadmin is quite straightforward, and no, i don't think it's cheating.
Creating the database structure
For this simple end-to-end communication POC, I’m just creating an articles table. Laravel has a very classic MVC approach, however, we are not creating the view parts, only the Model and Controller to interact with the database, through magic.
Using artisan, generate a super slim Laravel migration to create the actual table:
php artisan make:migration create_base_tables
and edit the generated file in /database/migrations/xxxx_xx_xx_xx_xxxxx_create_base_tables.php
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateBaseTables extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('articles', function($table)
{
$table->increments('id');
$table->string('title', 100);
$table->text('body');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('articles');
}
}
running it will generate a table in your database, with the defined properties:
php artisan migrate
Create an Eloquent Model in /models/Article.php
class Article extends Eloquent
{
protected $table = 'articles'; // the table name
}
And create the controller for this model, in /controllers/ArticlesController.php
<?php
class ArticlesController extends \BaseController {
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
return Article::all();
}
}
This should be it, for Laravel.
Add the Angularjs files
It’s too dangerous to go any further, alone. Take this:
https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js
https://raw.githubusercontent.com/angular/bower-angular-route/master/angular-route.min.js
First, setup Laravel's /app/views/index.php
file. This will be the main file onto where all the other pages will be re-rendered into. Very SPA. To hook it up AngularJS style, this is my base index.php
structure:
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF8">
<title>It’s a blog</title>
<!-- Add the usual things here -->
</head>
<body>
<div id="maincontent">
<div id="view" ng-view></div>
</div>
<script src="/vendor/angular/angular.min.js"></script>
<script src="/vendor/angular-route/angular-route.min.js"><<script>
<script src="/myApp.js"></script>
</body>
</html>
Note the myApp module declaration on the html block, and the ng-view
directive, inside the body. The first one links the html code with some module (to be created) and it's many parts; The ng-view
will be (in a very basic explanation) responsible for re-rendering different templates and placing them onto the html code. The myApp.js file will have the core angular code.
I chose to add all the AngularJS code on the /public
folder. Something like this:
/public
/css
/js
/controllers
/directives
/packages
/services
myApp.js
/templates
/vendor
Make the link
The base structure is setup and hopefully working. Although incredibly simple, it’s easily scalable, following the exact same logic. All that’s left is to introduce Angular to Laravel.
Make Angular talk to Laravel
Having the file structure setup, what's left is to...well, is to actually code something.
The myApp.js
file will contain the base Angular code (later should be sliced into the separated modular folders, but for the sake of this article, it wont).
I'll add the routing and the controller here:
var app = angular.module("myApp", ['ngRoute']).config(function($routeProvider){
$routeProvider.when('/index', {
templateUrl: 'templates/article-list.html',
controller: 'ArticleListController'
});
$routeProvider.otherwise({redirectTo : '/index'});
});
app.controller('ArticleListController', function ($scope, $http){
var endpoint = '/articles';
$http.get(endpoint).success(function ($articles) {
$scope.articles = $articles;
});
});
The ngRoute
module will be responsible for the routing, in simple terms, matching a URL to a HTML template and an AngularJS controller.
The controllers are added to the app module, and control the data on the template. The data is fetched through a GET request, on a specific endpoint. For this to work the $http service must be injected.
Make Laravel talk back to AngularJS
The Router.php
file sits in /app and i'll use it to match a RESTfull request to a Laravel controller. To answer the myApp.js
GET request:
Route::get('/', function(){
return View::make('index');
});
Route::get('articles', 'ArticlesController@index');
Laravel's articles were very cleverly created in app/controllers
, a while ago.
A GET request on /articles will trigger Laravel's ArticlesController index method, returning, in this case, a JSON representation of a list of articles:
public function index()
{
return Article::all();
}
This response will then trigger the .success callback function in myApp.js
, adding the articles to the `$scope.
One can add a lot of complexity to this logic, but, the basis remains the same.