This tutorial is out of date and no longer maintained.
Laravel and Angular have both become very well renowned tools in the web development world lately. Laravel for the great things it brings to the PHP community and Angular for the amazing frontend tools and its simplicity. Combining these two great frameworks only seems like the logical next step.
For our use cases, we will be using Laravel as the RESTful API backend and Angular as the frontend to create a very simple single-page comment application.
This will be a simple example to show off how to get started using these two technologies so don’t hope for any extra database stuff on how to handle sub-comments or anything like that.
This will be a simple single-page comment application:
Overall, these are very simple concepts. Our focus will be to see the intricacies of how Laravel and Angular can work together.
Go ahead and get your Laravel setup ready. We’ll be doing some basic things to get our backend to do CRUD on comments:
We will need a simple structure for our comments. We just need text
and author
. Let’s create our Laravel migration to create our comments.
Let’s run the artisan command that will create our comments migration so that we can create the table in our database:
- php artisan migrate:make create_comments_table --create=comments
We’ll use the Laravel Schema Builder to create the text
and author
fields that we need. Laravel will also create the id
column and the timestamps
so that we know how long ago the comment was made. Here is the code for the comments table:
...
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('comments', function(Blueprint $table)
{
$table->increments('id');
$table->string('text');
$table->string('author');
$table->timestamps();
});
}
...
Make sure you go adjust your database settings in app/config/database.php
with the right credentials. Now we will run the migration so that we create this table with the columns that we need:
- php artisan migrate
With our table made, let’s create an Eloquent model so that we can interact with it.
We will be using Laravel Eloquent models to interact with our database. This will be very easy to do. Let’s create a model: app/models/Comment.php
.
<?php
class Comment extends Eloquent { // let eloquent know that these attributes will be available for mass assignment protected $fillable = array('author', 'text'); }
We now have our new table and model. Let’s fill it with some sample data using Laravel Seeding.
We will need a few comments so that we can test a few things. Let’s create a seed file and fill our database with 3 sample comments.
Create a file: app/database/seeds/CommentTableSeeder.php
and fill it with this code.
<?php
class CommentTableSeeder extends Seeder {
public function run()
{
DB::table('comments')->delete();
Comment::create(array(
'author' => 'Chris Sevilleja',
'text' => 'Comment by Chris.'
));
Comment::create(array(
'author' => 'Nick Cerminara',
'text' => 'Comment by Nick.'
));
Comment::create(array(
'author' => 'Holly Lloyd',
'text' => 'Comment by Holly.'
));
}
}
To call this Seeder file, let’s open app/database/seeds/DatabaseSeeder.php
and add the following:
...
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Eloquent::unguard();
$this->call('CommentTableSeeder');
$this->command->info('Comment table seeded.');
}
...
Now let’s run our seeders using artisan.
- php artisan db:seed
Now we have a database with a comment table, an Eloquent model, and samples in our database. Not bad for a day’s work… but we’re not even close to done yet.
app/controllers/CommentController.php
We will use Laravel’s resource controllers to handle our API functions for comments. Since we’ll be using Angular to display a resource and show create and update forms, we’ll create a resource controller with artisan without the create
or edit
functions.
Let’s create our controller using artisan.
- php artisan controller:make CommentController --only=index,store,destroy
For our demo app, we’ll only be using these three functions in our resource controller. To expand on this you’d want to include all the functions like update, show, update for a more fully-fledged app.
Now we’ve created our controller. We don’t need the create
and edit
functions because Angular will be handling showing those forms, not Laravel. Laravel is just responsible for sending data back to our frontend. We also took out the update
function for this demo just because we want to keep things simple. We’ll handle creating, showing, and deleting comments.
To send data back, we will want to send all our data back as JSON. Let’s go through our newly created controller and fill out our functions accordingly.
<?php
class CommentController extends BaseController {
/**
* Send back all comments as JSON
*
* @return Response
*/
public function index()
{
return Response::json(Comment::get());
}
/**
* Store a newly created resource in storage.
*
* @return Response
*/
public function store()
{
Comment::create(array(
'author' => Input::get('author'),
'text' => Input::get('text')
));
return Response::json(array('success' => true));
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return Response
*/
public function destroy($id)
{
Comment::destroy($id);
return Response::json(array('success' => true));
}
}
You can see how easy it is to handle CRUD with Laravel and Eloquent. It’s incredibly simple to handle all the functions that we need.
With our controller ready to go, the last thing we need to do for our backend is routing.
Extra Reading: Simple Laravel CRUD with Resource Controllers
With our database ready to rock and roll, let’s handle the routes of our Laravel application. We will need routes to send users to the Angular frontend since that will have its own routing. We will also need routes for our backend API so people can access our comment data.
Let’s create the Angular pointing routes. We will need one for the home page and a catch-all route to send users to Angular. This ensures that any way a user accesses our site, they will be routed to the Angular frontend.
We’ll be prefixing our API routes with… (drumroll please)… api
. This way, if somebody wants to get all comments, they will use the URL: http://example.com/api/comments
. This just makes sense moving forward and is some basic API creation good tactics.
<?php
// HOME PAGE ===================================
// we dont need to use Laravel Blade
// we will return a PHP file that will hold all of our Angular content
// see the "Where to Place Angular Files" below to see ideas on how to structure your app return
Route::get('/', function() {
View::make('index'); // will return app/views/index.php
});
// API ROUTES ==================================
Route::group(array('prefix' => 'api'), function() {
// since we will be using this just for CRUD, we won't need create and edit
// Angular will handle both of those forms
// this ensures that a user can't access api/create or api/edit when there's nothing there
Route::resource('comments', 'CommentController',
array('only' => array('index', 'store', 'destroy')));
});
// CATCH ALL ROUTE =============================
// all routes that are not home or api will be redirected to the frontend
// this allows angular to route them
App::missing(function($exception) {
return View::make('index');
});
We now have our routes to handle the 3 main things our Laravel backend needs to do.
Handling Catch-All Routes: In Laravel, you can do this a few ways. Usually, it isn’t ideal to do the above code and have a catch-all for your entire application. The alternative is that you can use Laravel Controller Missing Methods to catch routes.
Testing All Our Routes Let’s make sure we have all the routes we need. We’ll use artisan and see all our routes:
- php artisan routes
This command will let us see our routes and sort of a top-down view of our application.
We can see the HTTP verb and the route used to get all comments, get a single comment, create a comment, and destroy a comment. On top of those API routes, we can also see how a user gets routed to our Angular application by the home page route.
Finally! Our Laravel API backend is done. We have done so much and yet, there’s still so much to do. We have set up our database and seeded it, created our models and controllers, and created our routes. Let’s move on to the frontend Angular work.
I’ve seen this question asked a lot. Where exactly should I be putting Angular files and how does Laravel and Angular work together. We did an article on Using Laravel Blade with AngularJS. This article works under the assumption that we aren’t even going to use Blade.
To let Angular handle the frontend, we will need Laravel to pass our user to our index.php
file. We can place this in a few different places. By default, when you use:
Route::get('/', function() {
return View::make('index');
});
This will return app/views/index.php
. Laravel will by default look in the app/views
folder.
Some people may want to keep Angular files completely separate from Laravel files. They will want their entire application to be housed inside of the public
folder. To do this is simple: just change the default View location to the public folder. This can be done in the app/config/view.php
file.
...
// make laravel look in public/views for view files
'paths' => array(__DIR__.'/../../public/views'),
...
Now return View::make('index')
will look for public/views/index.php
. It is all preference on how you’d like to structure your app. Some people see it as a benefit to have the entire Angular application in the public folder so that it is easier to handle routing and if it is needed in the future, to completely separate the backend RESTful API and the Angular frontend.
For Angular routing, then your partial files will be placed in the public folder, but that’s out of the scope of this article. For more information on that kind of single-page Angular routing, check out Single Page Angular Application Routing.
Let’s assume we left everything default and our main view file is in our app/views
folder and move forward.
Routing with Laravel and Angular There are a lot of questions about having routing with Laravel and Angular and if they conflict. Laravel will handle the main routing for your application. Angular routing will only happen when Laravel routes our user to the main Angular route (index.php
) in this case. This is why we use a Laravel catch-all route. Laravel will handle the API routes and anything it doesn’t know how to route will be sent to Angular. You can then set up all the routing for your Angular application to handle showing different views.
Everything for our Angular application will be handled in the public
folder. This lets us keep a good separation of the backend in the app
folder.
Let’s look at the application structure we will have in our public
folder. We’ve created our Angular application to be modular since that is best practice. Now our separated parts of our application will be easy to test and work with.
- public/
----- js/
---------- controllers/ // where we will put our angular controllers
--------------- mainCtrl.js
---------- services/ // angular services
--------------- commentService.js
---------- app.js
public/js/services/commentService.js
Our Angular service is going to be the primary place where we will have our HTTP calls to the Laravel API. It is pretty straightforward and we use the Angular $http service.
angular.module('commentService', [])
.factory('Comment', function($http) {
return {
// get all the comments
get : function() {
return $http.get('/api/comments');
},
// save a comment (pass in comment data)
save : function(commentData) {
return $http({
method: 'POST',
url: '/api/comments',
headers: { 'Content-Type' : 'application/x-www-form-urlencoded' },
data: $.param(commentData)
});
},
// destroy a comment
destroy : function(id) {
return $http.delete('/api/comments/' + id);
}
}
});
This is our Angular service with 3 different functions. These are the only functions we need since they will correspond to the API routes we made in our Laravel routes.
We will be returning the promise object from our service. These will be dealt with in our controllers. The naming convention here also stays the same as the Laravel controller that we have.
With our Angular Service done, let’s go into our controller and use it.
public/js/controllers/mainCtrl.js
The controller is where we will have most of the functionality for our application. This is where we will create functions to handle the submit forms and deleting on our view.
angular.module('mainCtrl', [])
// inject the Comment service into our controller
.controller('mainController', function($scope, $http, Comment) {
// object to hold all the data for the new comment form
$scope.commentData = {};
// loading variable to show the spinning loading icon
$scope.loading = true;
// get all the comments first and bind it to the $scope.comments object
// use the function we created in our service
// GET ALL COMMENTS ==============
Comment.get()
.success(function(data) {
$scope.comments = data;
$scope.loading = false;
});
// function to handle submitting the form
// SAVE A COMMENT ================
$scope.submitComment = function() {
$scope.loading = true;
// save the comment. pass in comment data from the form
// use the function we created in our service
Comment.save($scope.commentData)
.success(function(data) {
// if successful, we'll need to refresh the comment list
Comment.get()
.success(function(getData) {
$scope.comments = getData;
$scope.loading = false;
});
})
.error(function(data) {
console.log(data);
});
};
// function to handle deleting a comment
// DELETE A COMMENT ====================================================
$scope.deleteComment = function(id) {
$scope.loading = true;
// use the function we created in our service
Comment.destroy(id)
.success(function(data) {
// if successful, we'll need to refresh the comment list
Comment.get()
.success(function(getData) {
$scope.comments = getData;
$scope.loading = false;
});
});
};
});
As you can see in our controller, we have injected our Comment
service and use it for the main functions: get
, save
, and delete
. Using a service like this helps to not pollute our controller with $http
gets and puts.
public/js/app.js
On the Angular side of things, we have created our service and our controller. Now let’s link everything together so that we can apply it to our application using ng-app
and ng-controller
.
This will be the code to create our Angular application. We will inject the service and controller into. This is a best practice since it keeps our application modular and each different part can be testable and extendable.
var commentApp = angular.module('commentApp', ['mainCtrl', 'commentService']);
That’s it! Not much to it. Now we’ll actually get to our view where we can see how all these Angular parts work together.
app/views/index.php
So far, after everything we’ve done up to this point, we still won’t be able to see anything in our browser. We will need to define our view file since Laravel in our home route and our catch-all route returns return View::make('index');
.
Let’s go ahead and create that view now. We will be using all the Angular parts that we’ve created. The main parts that we’ve created from Angular that we’ll use in index.php
are:
body
tagng-submit
loading
. If it is set to true, we’ll show a loading icon and hide the commentsNow let’s get to the actual code for our view. We’ll comment out the main important parts so we can see how everything works together.
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Laravel and Angular Comment System</title>
<!-- CSS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css"> <!-- load bootstrap via cdn -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css"> <!-- load fontawesome -->
<style>
body { padding-top:30px; }
form { padding-bottom:20px; }
.comment { padding-bottom:20px; }
</style>
<!-- JS -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular.min.js"></script> <!-- load angular -->
<!-- ANGULAR -->
<!-- all angular resources will be loaded from the /public folder -->
<script src="js/controllers/mainCtrl.js"></script> <!-- load our controller -->
<script src="js/services/commentService.js"></script> <!-- load our service -->
<script src="js/app.js"></script> <!-- load our application -->
</head>
<!-- declare our angular app and controller -->
<body class="container" ng-app="commentApp" ng-controller="mainController"> <div class="col-md-8 col-md-offset-2">
<!-- PAGE TITLE =============================================== -->
<div class="page-header">
<h2>Laravel and Angular Single Page Application</h2>
<h4>Commenting System</h4>
</div>
<!-- NEW COMMENT FORM =============================================== -->
<form ng-submit="submitComment()"> <!-- ng-submit will disable the default form action and use our function -->
<!-- AUTHOR -->
<div class="form-group">
<input type="text" class="form-control input-sm" name="author" ng-model="commentData.author" placeholder="Name">
</div>
<!-- COMMENT TEXT -->
<div class="form-group">
<input type="text" class="form-control input-lg" name="comment" ng-model="commentData.text" placeholder="Say what you have to say">
</div>
<!-- SUBMIT BUTTON -->
<div class="form-group text-right">
<button type="submit" class="btn btn-primary btn-lg">Submit</button>
</div>
</form>
<!-- LOADING ICON =============================================== -->
<!-- show loading icon if the loading variable is set to true -->
<p class="text-center" ng-show="loading"><span class="fa fa-meh-o fa-5x fa-spin"></span></p>
<!-- THE COMMENTS =============================================== -->
<!-- hide these comments if the loading variable is true -->
<div class="comment" ng-hide="loading" ng-repeat="comment in comments">
<h3>Comment #{{ comment.id }} <small>by {{ comment.author }}</h3>
<p>{{ comment.text }}</p>
<p><a href="#" ng-click="deleteComment(comment.id)" class="text-muted">Delete</a></p>
</div>
</div>
</body>
</html>
Now we finally have our view that brings all of the parts we created together. You can go ahead and play around with the application. All the parts should fit together nicely and creating and deleting comments should be done without a page refresh.
Make sure you take a look at the GitHub repo to test the application. Here are some quick instructions to get you going.
git clone git@github.com:scotch-io/laravel-angular-comment-app
composer install --prefer-dist
app/config/database.php
php artisan migrate
php artisan db:seed
Hopefully, this tutorial gives a good overview of how to start an application using Laravel and Angular. You can bring this further and create a full application that can handle multiple API calls on the Laravel side, and even create your own Angular routing for multiple pages.
Sound off in the comments if you have any questions or would like to see a specific use case. We can also expand on this demo and start adding different things like editing a comment, user profiles, whatever.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!