When creating a single-page app we should use some kind of framework to do some of the job for us, so we can focus on the actual functionality. AngularJS fits here perfectly, because features like dynamic dependency injection and bi-directional data binding are just great. Sometimes we also require some kind of server. If you've chosen PHP then Laravel may be your best option, as it's easy to work with and pretty powerful.
Introduction
In this tutorial you will create a simple customer/transaction management system with the ability to add and remove both transactions and customers. This is probably not the kind of thing you make very often, but it shows how to use features of both frameworks.
Before we start you should setup a MySQL database which we will use (Laravel supports many more of them, but this is still the most popular one). You don't need any web server since we will be using PHP's built-in one (but please keep in mind, that this solution is only for the development and should never be used in production - it lacks many features that are required for your app to work properly in public). For that, we will need at least PHP version 5.4.0.
Preparation
The first thing we have to do is to install Laravel. The full process is described on Laravel's website. After that, you should have your project directory created with all of Laravel's files in there. Navigate to that directory in your command line and run this command there:
php artisan serve
If all goes OK, you should see that the local development server was started on locahost:8000
. Open your browser and navigate there, you should see Laravel's welcome page:
Now we can proceed to the actual application.
Migrations and Models
Models in Laravel are just like in any other MVC framework. It's using the Eloquent ORM to ease the work for you - you will probably never need to write an SQL query again (unless you'll want something that Eloquent does not support). Using migrations you can modify the database structure with the ability to rollback changes if something goes wrong. You can read more about migrations in the documentation.
In our app we will use two models:
-
Customer
- will hold the customer data -
Transaction
- will hold the information about a transaction
Let's start by creating migrations for them. If you have not done so already, shut down the server we started earlier (Ctrl + C).
Customers
First, invoke this command:
php artisan migrate:make create_customers_table
This will create a migration file with a basic structure for you. Now navigate to app/database/migrations
. There should be a file with its name starting with a timestamp and ending with "create_customers_table". Laravel automatically created this basic structure for you. The up()
method is called when the migration is applied, and down()
when it's rolled back.
First call the Schema::create()
method. It takes two arguments - the schema's name and a callback function:
Schema::create('customers', function ($table) {
The callback is executed when the table is created. The table object is passed as the $table
variable and we manipulate the table's structure using it. Let's add an auto-incrementing id
field:
$table->increments('id');
Next there will be three string fields for the customer's first name, surname and email:
$table->string('first_name'); $table->string('last_name'); $table->string('email')->unique();
We make the email
field unique by calling the unique()
method on it.
The last method is for the timestamps:
$table->timestamps(); });
This will create two date fields in the schema: created_at
and updated_at
. These will be used by Eloquent to store the time when the item was created and updated.
Finally, the code should look like this:
public function up() { Schema::create('customers', function ($table) { $table->increments('id'); $table->string('first_name'); $table->string('last_name'); $table->string('email')->unique(); $table->timestamps(); }); }
The down()
method is much simpler - it just deletes the schema:
public function down() { Schema::drop('customers'); }
Transactions
The code here will be similar to the customers' one. First invoke this command:
php artisan migrate:make create_transactions_table
Now locate the appropriate file in the app/database/migrations
and open it. Like earlier, start by creating the schema:
Schema::create('transactions', function ($table) {
Now add the fields for the id, transaction's name, its cost and the id of the customer that it belongs to:
$table->increments('id'); $table->string('name'); $table->float('amount'); $table->integer('customer_id');
And of course the timestamps:
$table->timestamps(); });
The final code should look like this:
public function up() { Schema::create('transactions', function ($table) { $table->increments('id'); $table->string('name'); $table->float('amount'); $table->integer('customer_id'); $table->timestamps(); }); }
And now the down()
method:
public function down() { Schema::drop('transactions'); }
Database Configuration
Now before you apply the migrations you'll have to configure the connection to your database. Open the app/config/database.php
file and go to line 55. Here is the configuration data for MySQL (there are few others in there, for example you could use SQLite or Postgres):
'mysql' => array( 'driver' => 'mysql', // database driver, don't touch 'host' => 'localhost', // host of the database, usually localhost unless you have your db on some server 'database' => 'database', // name of the database you will be using, it must be created earlier 'username' => 'root', // username that the script will use to connect, I strongly advice against using root user for this 'password' => '', // password for the user above, it's better not to use a blank one 'charset' => 'utf8', // encoding of the db 'collation' => 'utf8_unicode_ci', // db's collation setting 'prefix' => '', // prefix of the database tables, useful if you have multiple scripts using the same database ),
After you have filled that in, you are good to go. Make sure you saved the file and invoke this command from your app's main directory (the one with the artisan
file in it):
php artisan migrate
And thats it. If there were no errors, that means that the tables were created successfully. You can connect to your db using, for example, phpMyAdmin to check manually if you want.
Models
In Laravel, creating a model after you've configured your database using migrations is really quick. Navigate to app/models
and delete the example User.php
file that is there. Now create two files named Customer.php
and Transaction.php
.
Let's start with Customer.php
. Every model in Laravel has to extend the Eloquent
class:
class Customer extends Eloquent {
Now we will define a relationship between the customer and their transactions. This is done by defining a public method in the model with the name of the property we would like to have in it (in this case transactions
):
public function transactions() {
Now in the body of the function there will be only one line:
return $this->hasMany('Transaction'); } }
This tells Eloquent that it should provide all transactions with customer_id
of the customer under a property named transactions
.
Now we will do pretty much the same for the transactions, but we will reverse the relationship to make the transaction's owner accessible via the customer
property:
class Transaction extends Eloquent { public function customer() { return $this->belongsTo('Customer'); } }
This is done using the $this->belongsTo()
method of the model.
Controllers
Now to actually use the models we have to create controllers for them. Head to the app/controllers
directory, delete the HomeController.php
only - BaseController.php
is important as our controllers will extend it. Now create two files: CustomerController.php
and TransactionController.php
.
CustomerController
This controller will handle everything related to the customers - adding, removing and showing a list of them. Start by defining the class:
class CustomerController extends BaseController {
We will be using Laravel's feature named RESTful controllers. It makes creating routes easier because we only have to define the base URI and Laravel will handle everything for us. This requires you to start your function names with the appropriate HTTP verb and then continue with the subroute name (using camelCase). So for example, if we would have a method named getNames
and the base URI would be /customers
, then the method will be accessible at /customers/names
.
The getIndex()
, postIndex()
, deleteIndex()
etc. methods will be mapped to the default route (in this case /customers
).
Now let's define our first route - getting the customer by their id:
public function getIndex() {
Let's get the id from the query parameters (Laravel provides a nice Input
class to deal with that, so you don't have to use $_GET
, $_POST
and $_FILES
):
$id = Input::get('id');
And search for the user in the database using that id:
return Customer::find($id); }
Every method of the controller has to return a value that is a string or has a __toString()
method. In this case the Customer
model that is returned will be converted to JSON before sending.
Now lets return a list of all users (this will be accessible under /customers/all
):
public function getAll() { return Customer::all(); }
As you can see, we can get all customers using the model's all()
method.
Now the longest part, adding a new customer:
public function postIndex() {
First let's check if all information needed was provided. We can do this using the Input::has()
method:
if (Input::has('first_name', 'last_name', 'email')) {
Let's put all of the input fields in the $input
variable to avoid calling Input::get()
over and over. This can be done using Input::all()
:
$input = Input::all();
Next we will check if any of the inputs are empty. If so, we will return a HTTP 400 Bad Request error with a more verbose message:
if ($input['first_name'] == '' || $input['last_name'] == '' || $input['email'] == '') { return Response::make('You need to fill all of the input fields', 400); }
Since we wanted to return a status code other than 200 instead of just returning the message as a string, we used Response::make()
, which takes the data to send as the first parameter and the status code as the second. Take a look at the docs if you want to know more about responses.
Now we finally create a new Customer
model and feed it with the data provided:
$customer = new Customer; $customer->first_name = $input['first_name']; $customer->last_name = $input['last_name']; $customer->email = $input['email'];
After that we can save the newly created model and respond to the request with it:
$customer->save(); return $customer;
Here we handle the case if not all of the inputs were provided:
} else { return Response::make('You need to fill all of the input fields', 400); } }
Finally, we also need the ability to remove the customers. This one is really short:
public function deleteIndex() {
We start by getting the id of the customer to delete:
$id = Input::get('id');
Next, we search for and delete the customer:
$customer = Customer::find($id); $customer->delete();
After that, we respond to the request with the id provided:
return $id; } }
Now before the routes can be accessed, we have to hook them. Open the app/routes.php
file, delete everything but the comment and add this line at the end of the file:
Route::controller('/customers', 'CustomerController');
This will tell Laravel to route all requests at /customers
to our CustomerController
. Now you can use CURL to play with it. First start the server with php artisan serve
and then you can, for example, create a customer:
curl -X POST -d "first_name=Jane&last_name=Doe&[email protected]" http://localhost:8000/customers
Then you can get the list of all customers:
curl http://localhost:8000/customers/all
TransactionController
This, like the model is very similar to the CustomerController
. First create the class:
class TransactionController extends BaseController {
Then let's define the method to get all transactions for a user:
public function getIndex() { $id = Input::get('id'); return User::find($id)->transactions; }
As you can see we are using the relationship defined earlier to get the transactions (now recall the query you had to write to achieve the same thing using plain PHP and SQL).
The next thing will be the creation of transactions:
public function postIndex() {
Like earlier, we are checking if all of the required information is provided:
if (Input::has('name', 'amount')) {
If so, assign it to an $input
variable:
$input = Input::all();
Check if any of the values provided are empty and if so return an error:
if ($input['name'] == '' || $input['amount'] == '') { return Response::make('You need to fill all of the input fields', 400); }
Now create the transaction and supply it with all of the info provided:
$transaction = new Transaction; $transaction->name = $input['name']; $transaction->amount = $input['amount'];
Now we need to add it to the appropriate customer. Let's find them by the id provided and add the $transaction
to their transactions list:
$id = $input['customer_id']; User::find($id)->transactions->save($transaction);
This is done using the transactions->save()
method provided by Laravel. Now we can respond with the transaction created:
return $transaction;
And handle the case where none or not all of the data was provided:
} else { return Response::make('You need to fill all of the input fields', 400); } }
After that there is also a method to delete the transaction in the same way that we deleted the customer:
public function deleteIndex() { $id = Input::get('id'); $transaction = Transaction::find($id); $transaction->delete(); return $id; } }
Now just add the route and you can test the controller using CURL:
Route::controller('/transactions', 'TransactionController');
Conclusion
Alright, this is the end of the first part - in the second part of this tutorial, we will create the front-end using AngularJS. Feel free to add more features to your app (like editing customers or sorting), in case you did not find the information you were looking for, take a look at Laravel's documentation.
Comments