This beginner tutorial/article shows how you can create a simple/basic CRUD (Create, Read, Update, Delete) system or application using Laravel. Laravel is a popular open-source PHP MVC Framework with lots of advanced development features.
Installing Laravel has been explained here.
Laravel Application Folder Structure
After you have successfully installed Laravel, you need to know about the application structure (folder structure) of Laravel. The root directory of Laravel contains the following directories. Sub-directories of app
directory is also listed below.
DIRECTORY DESCRIPTION
/app contains core code of your application
/app/Console contains all of your Artisan commands
/app/Events contains event classes
/app/Exceptions contains exception handling classes
/app/Http contains controllers, middleware, and requests
/app/Jobs contains jobs that can be queued
/app/Listeners contains handler classes for events
/app/Policies contains authorization policy classes
/bootstrap contains files required by the bootstrap framework and configure autoloading
/config contains the application configuration files
/database contains database migrations and seeds. It is also used to store the SQLite database.
/public contains the front controllers and assets like images, CSS, JavaScript etc.
/storage contains compiled blade templates, filed based sessions, file caches & other files generated by the framework.
/tests contains automated unit tests
/vendor contains composer dependencies
Artisan Command Line Tool
The other important thing to understand is the Artisan command line tool. Laravel consists of a command line interface named ‘Artisan’. It is based upon the Symfony Console Component.
Artisan contains a lot of helpful commands for application development. To view the list of all Artisan commands, you need to go to your Laravel root folder and run the following command:
php artisan list
You will see the following list of available commands after running the above command:
Laravel Framework version 5.2.41
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
--env[=ENV] The environment the command should run under.
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
clear-compiled Remove the compiled class file
down Put the application into maintenance mode
env Display the current framework environment
help Displays help for a command
list Lists commands
migrate Run the database migrations
optimize Optimize the framework for better performance
serve Serve the application on the PHP development server
tinker Interact with your application
up Bring the application out of maintenance mode
app
app:name Set the application namespace
auth
auth:clear-resets Flush expired password reset tokens
cache
cache:clear Flush the application cache
cache:table Create a migration for the cache database table
config
config:cache Create a cache file for faster configuration loading
config:clear Remove the configuration cache file
db
db:seed Seed the database with records
event
event:generate Generate the missing events and listeners based on registration
key
key:generate Set the application key
make
make:auth Scaffold basic login and registration views and routes
make:console Create a new Artisan command
make:controller Create a new controller class
make:event Create a new event class
make:job Create a new job class
make:listener Create a new event listener class
make:middleware Create a new middleware class
make:migration Create a new migration file
make:model Create a new Eloquent model class
make:policy Create a new policy class
make:provider Create a new service provider class
make:request Create a new form request class
make:seeder Create a new seeder class
make:test Create a new test class
migrate
migrate:install Create the migration repository
migrate:refresh Reset and re-run all migrations
migrate:reset Rollback all database migrations
migrate:rollback Rollback the last database migration
migrate:status Show the status of each migration
queue
queue:failed List all of the failed queue jobs
queue:failed-table Create a migration for the failed queue jobs database table
queue:flush Flush all of the failed queue jobs
queue:forget Delete a failed queue job
queue:listen Listen to a given queue
queue:restart Restart queue worker daemons after their current job
queue:retry Retry a failed queue job
queue:table Create a migration for the queue jobs database table
queue:work Process the next job on a queue
route
route:cache Create a route cache file for faster route registration
route:clear Remove the route cache file
route:list List all registered routes
schedule
schedule:run Run the scheduled commands
session
session:table Create a migration for the session database table
vendor
vendor:publish Publish any publishable assets from vendor packages
view
view:clear Clear all compiled view files
As you can see above, there are many commands that do different helpful jobs. You can clear Cache with a single command. You can run scheduled tasks, create different controller, events, etc. classes, and so on.
As an example, to clear your Laravel application cache, you can simply run the following command:
php artisan cache:clear
We will be using artisan commands later in this tutorial.
Create Database
After we have some understading of Laravel application/folder structure and about Artisan CLI, we can now move forward to create our basic CRUD (Create, Read, Update, Delete) application in Laravel.
First of all, we need to create a new database and a table inside the newly created database. We will name our database as test</strong>
and create a table named <strong>news</strong>
inside the database <strong>test
.
Here’s the MySQL query to create database:
CREATE DATABASE test;
Database Configuration Settings
After you create your database, you need to enter your database name and database login credentials in Laravel’s configuration settings file. Laravel’s database configuration file is present at /path-to-laravel-root/config/database.php
.
In this file, you will find connection defined for different databases like sqlite, mysql, and pgsql. We will be using MySQL database. Therefore, we will update the information for mysql database only.
The database name of our application in this tutorial is test</strong>
. The database username in my system is <strong>root</strong>
and password is also <strong>root</strong>
. Database host for my local machine is <strong>localhost</strong>
and port is <strong>3306
. Here is the update done by my side. You have to update it according to your database settings.
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'test'), // YOUR DATABASE NAME
'username' => env('DB_USERNAME', 'root'), // YOUR DATABASE USERNAME
'password' => env('DB_PASSWORD', 'root'), // YOUR DATABASE PASSWORD
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],
To use database commands from Artisan Command Line Tool, you also need to update database settings in /path-to-laravel-root/.env</strong>
file because Artisan fetches database settings from <strong>.env
file. Here’s the updated .env file on my computer:
DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=test
DB_USERNAME=root
DB_PASSWORD=root
Creating Table
You can create or modify tables simply through SQL queries or from GUI like phpMyAdmin. But, Laravel provides you another option to do so. It’s called Migrations
. With Laravel Migrations, you can create/modify database tables from PHP code.
We will be creating a table named news
. Here are the steps to do so:
- Open terminal/command-prompt
- Go to your laravel root directory.
- In my case (Ubuntu 16.04) it will be:
cd /var/www/html/laravel
- Run the following command:
php artisan migrate:install
This will create migrations</strong>
table in your <strong>test
database.
- Now, run the following command:
php artisan make:migration create_news_table
This will create a new class named CreateNewsTable</strong>
at <strong>your-laravel-root/database/migrations/2016_08_26_114034_create_news_table.php
. create_news_table name is preceded by date value.
CreateNewsTable</strong>
class will have two empty functions <strong>up()</strong>
and <strong>down()
. Table creation code should be added in up() function and table deletion code should be added in down() function.
Here is the updated CreateNewsTable
class:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateNewsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// create table 'news'
Schema::create('news', function (Blueprint $table) {
$table->increments('id');
$table->string('title',255);
$table->string('slug',100)->unique();
$table->text('short_description');
$table->text('full_content');
$table->string('author',100);
$table->string('category',100);
$table->timestamps(); // timestamps() creates created_at & updated_at fields
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// drop table 'news'
Schema::drop('news');
}
}
Note: Running the following command will add basic schema code to up() and down() function of migration class
php artisan make:migration create_news_table --create=news
Run the following command to execute the migration class
php artisan migrate
The table news
has been created.
Adding Test Data to Database Table
This is optional. If you wish to add sample/test data to your database table then you can do this with the Laravel’s seed classes. This method is also called Database Seeding
.
In this example, we will be adding some test data to your news
table.
- Run the following command on terminal/command-prompt:
php artisan make:seeder NewsTableSeeder
This will create a new class named NewsTableSeeder</strong>
at <strong>your-laravel-root/database/seeds/NewsTableSeeder.php
.
NewsTableSeeder</strong>
class will have an empty function <strong>run()
. Data insertion code should be added in the run() function.
Here is the updated NewsTableSeeder
class:
<?php
use Illuminate\Database\Seeder;
class NewsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('news')->insert([
'title' => 'Lorem ipsum dolor sit',
'slug' => 'lorem-ipsum',
'short_description' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
'full_content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla dui nunc, condimentum a volutpat consectetur, pulvinar sit amet turpis. Donec finibus eu risus eget porta. Proin rutrum at risus vel viverra. Aliquam sit amet hendrerit orci. Sed volutpat vitae ante ac mattis. Praesent id neque vestibulum, hendrerit mauris eget, pretium mauris. Proin gravida nisi turpis, quis ornare eros rutrum id. Ut cursus ultricies semper. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nunc sodales erat leo, ac laoreet erat placerat sit amet. In eget libero mi. Integer ut faucibus dolor.',
'author' => 'John',
'category' => 'Sports',
'created_at' => date("Y-m-d H:i:s"),
'updated_at' => date("Y-m-d H:i:s"),
]);
}
}
Run the following command to execute run()</strong>
function of <strong>NewsTableSeeder</strong>
class. This will add data to <strong>news
table.
php artisan db:seed --class=NewsTableSeeder
Using Faker PHP Library to generate fake data
We can also use Faker Library to generate fake data for our database table. Here is the NewsTableSeeder class using Faker library:
<?php
use Illuminate\Database\Seeder;
class NewsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$faker = Faker\Factory::create();
// add 10 rows of random data
for ($i=0; $i < 10; $i++) {
DB::table('news')->insert([
'title' => $faker->sentence(),
'slug' => $faker->slug,
'short_description' => $faker->paragraph(),
'full_content' => $faker->paragraphs(3, true), // 3 paragraphs
'author' => $faker->name,
'category' => $faker->colorName, // color name as category
'created_at' => $faker->dateTime(),
'updated_at' => $faker->dateTime(),
]);
}
}
}
Creating Model
Model classes are used for database operations like querying database tables or inserting/updating records in the database table. Laravel uses the Eloquent ORM (Object Relational Mapper) for implementing database tasks. In this, each database table is associated with a Model class.
Run the following command to create the Model class for table news
:
php artisan make:model News
The News</strong>
model class will be created at <strong>your-laravel-root-folder/app/News.php
.
You need to add the table name, primary key of the table, etc. in the News
class.
Here’s the updated News
class:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class News extends Model
{
/**
* The table associated with the model
*
* @var string
*/
protected $table = 'news';
/**
* Indicates primary key of the table
*
* @var bool
*/
public $primaryKey = 'id';
/**
* Indicates if the model should be timestamped
*
* default value is 'true'
*
* If set 'true' then created_at and updated_at columns
* will be automatically managed by Eloquent
*
* If created_at and updated_at columns are not in your table
* then set the value to 'false'
*
* @var bool
*/
public $timestamps = true;
/**
* The attributes that are mass assignable
*
* @var array
*/
protected $fillable = array('title', 'slug', 'short_description', 'full_content', 'author', 'category', 'created_at', 'updated_at');
/**
* The attributes that aren't mass assignable
*
* @var array
*/
protected $guarded = array();
}
Routing
Before moving on to Controller, we first look into routes file. The route file is present at app/Http/routes.php
. Generally, the use of route file is to connect request URL to Controller methods.
Here is a basic route example:
Route::get('foo', function () {
return 'Hello World';
});
You will get ‘Hello World’ printed when you browse http://your-laravel-site/foo
In the above route, we wrote some static code. We will now write a new route that will link to a function/action of our NewsController class.
Route::get('news', array('as' => 'news.index', 'uses' => 'NewsController@index'));
This will route to index() function of News controller class when we browse http://your-laravel-site/news
For our application, we need to put the following code in our application’s app/Http/routes.php
file.
Route::get('news', array('as' => 'news.index', 'uses' => 'NewsController@index'));
Route::get('news/add', array('as' => 'news.create', 'uses' => 'NewsController@create'));
Route::post('news/store', array('as' => 'news.store', 'uses' => 'NewsController@store'));
Route::get('news/edit/{id}', array('as' => 'news.edit', 'uses' => 'NewsController@edit'));
Route::patch('news/update/{id}', array('as' => 'news.update', 'uses' => 'NewsController@update'));
Route::delete('news/delete/{id}', array('as' => 'news.destroy', 'uses' => 'NewsController@destroy'));
Route::get('news/{slug}', array('as' => 'news.show', 'uses' => 'NewsController@show'));
Creating Controller
In Controller class, we write all the logics to fetch data from database table, process it and pass it to views. Controller clasess are saved in app/Http/Controllers
directory.
Run the following code to create a NewsController
controller class.
php artisan make:controller NewsController --resource
This will create NewsController</strong>
class at <strong>app/Http/Controller/NewsController.php</strong>
. Using <strong>--resource
will create basic CRUD routes in the News controller class. It will create empty functions named index, create, update, destroy, etc.
In controller functions we call our model class, fetch the data, process it, and pass the data to view file.
In the index() function below, News data is first fetched and then the view template is returned with using view() method.
In store() function, form submit request is fetched, then the input data is saved into database using create() method and then redirected to news.index page.
Here is the updated NewsController
class used for this tutorial’s application:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use Session;
use App\News;
class NewsController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//$news = News::all();
// using paginate function to show 3 news items per page
$itemsPerPage = 3;
$news = News::orderBy('created_at', 'desc')->paginate($itemsPerPage);
return view('news.index', array('news' => $news, 'title' => 'News Display'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('news.create', array('title' => 'Add News'));
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, array(
'title' => 'required',
'slug' => 'required',
'short_description' => 'required',
'full_content' => 'required',
)
);
$input = $request->all();
//dd($input); // dd() helper function is print_r alternative
News::create($input);
Session::flash('flash_message', 'News added successfully!');
//return redirect()->back();
//return redirect('news');
return redirect()->route('news.index');
}
/**
* Display the specified resource.
*
* @param string $slug
* @return \Illuminate\Http\Response
*/
public function show($slug)
{
$news = News::where('slug', $slug)->first();
return view('news.show', array('news' => $news));
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$news = News::findOrFail($id);
return view('news.edit', array('news' => $news, 'title' => 'Edit News'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$news = News::findOrFail($id);
$this->validate($request, array(
'title' => 'required',
'slug' => 'required',
'short_description' => 'required',
'full_content' => 'required',
)
);
$input = $request->all();
$news->fill($input)->save();
Session::flash('flash_message', 'News updated successfully!');
return redirect()->back();
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$news = News::findOrFail($id);
$news->delete();
Session::flash('flash_message', 'News deleted successfully!');
return redirect()->route('news.index');
}
}
Views
Views are html files present in laravel-root/resources/views
directory. Laravel uses Blade templating engine in views which helps to use loops and if/else conditions like PHP in the view html file.
Laravel uses blade templating engine, so the views files should be named as viewname.blade.php
.
As you can see above, in index()</strong>
function of <strong>NewsController</strong>
class, we have passed news data into <strong>news.index</strong>
view. This means that the view should be stored in <strong>laravel-root/resources/views/news/index.blade.php
.
For this tutorial, we will first create a master template where we include/define the title, header, content and footer. We then extend the master template for other pages of our application. We also display success and error flash message on master template.
Here is our master template (laravel-root/resources/views/layouts/master.blade.php
):
<html>
<head>
<title>@yield('title')</title>
</head>
<body>
@section('header')
<a href="{{ route('news.index') }}">Home</a> | <a href="{{ route('news.create') }}">Add News</a>
@show
@if(Session::has('flash_message'))
<div style="color:green; border:1px solid #aaa; padding:4px; margin-top:10px">
{{ Session::get('flash_message') }}
</div>
@endif
@if($errors->any())
<div style="color:red; border:1px solid #aaa; padding:4px; margin-top:10px">
@foreach($errors->all() as $error)
{{ $error }}
@endforeach
</div>
@endif
<div>
@yield('content')
</div>
<div>
Footer @ 2016
</div>
</body>
</html>
resources/views/news/index.blade.php</strong>
will be extending master.blade.php template and then displaying news item in content section. The news data for index template comes from <strong>index()</strong>
function of <strong>NewsController
class.
In the index template, you will also see a Delete form. The delete action goes to destroy($id)</strong>
function of <strong>NewsController
class. Instead of link, button is to be used for Delete purpose in Laravel.
$news->links()
shows pagination links.
@extends('layouts.master')
@section('title', $title)
@section('sidebar')
@parent
// you can add something here
@endsection
@section('content')
<h1>News</h1>
@foreach ($news as $data)
<div style="width:60%; border-bottom:1px solid #aaa">
<a href="{{ route('news.show', $data->slug) }}"><strong>{{ $data->title }}</strong></a>
<br>
<div>
<span style="float:left">
Category: {{ $data->category }} | Author: {{ $data->author }} | Published on: {{ $data->created_at }} |
<a href="{{ route('news.edit', $data->id) }}">Edit</a>
</span>
<!-- Delete should be a button -->
{!! Form::open(array(
'method' => 'DELETE',
'route' => ['news.destroy', $data->id],
'onsubmit' => "return confirm('Are you sure you want to delete?')",
))
!!}
{!! Form::submit('Delete') !!}
{!! Form::close() !!}
<!-- End Delete button -->
</div>
{{ $data->short_description }}
</div>
@endforeach
<!-- Showing Pagination Links -->
<style>
ul {display:inline-block}
li {display:inline; padding:5px}
</style>
<div> {{ $news->links() }} </div>
<!-- End Showing Pagination Links -->
@endsection
The news index page will look like below:
Now, we move towards creating an add news form. We can simply write the html syntax to create the form. But, Laravel also provides us an option to create form using Illuminate/Html
package.
To install this package, update your-laravel-root/composer.json
file. Add this:
{
"require": {
"laravelcollective/html": "^5.2"
}
}
Then run composer update command on terminal:
composer update
This will install the laravelcollective/html package to your laravel application.
After that, edit laravel-root/config/app.php
.
'providers' => [
...
...
Collective\Html\HtmlServiceProvider::class,
...
...
],
'aliases' => [
...
...
'Form' => Collective\Html\FormFacade::class,
'Html' => Collective\Html\HtmlFacade::class,
],
Now, create template file to add news. The add news form post action goes to store()</strong>
function of <strong>NewsController
class.
laravel-root/resources/views/news/create.blade.php
@extends('layouts.master')
@section('title', $title)
@section('sidebar')
@parent
// you can add something here
@endsection
@section('content')
<h1>{{ $title }}</h1>
{!! Form::open([
'route' => 'news.store'
]) !!}
<table>
<tr>
<td>{!! Form::label('title', 'Title', ['class' => 'control-label']) !!}</td>
<td>{!! Form::text('title', null, ['class' => 'form-control', 'size' => 64, ]) !!}</td>
</tr>
<tr>
<td>{!! Form::label('slug', 'Slug', ['class' => 'control-label']) !!}</td>
<td>{!! Form::text('slug', null, ['class' => 'form-control', 'size' => 64, ]) !!}</td>
</tr>
<tr>
<td>{!! Form::label('category', 'Category', ['class' => 'control-label']) !!}</td>
<td>{!! Form::select('category', array('Politics' => 'Politics', 'Sports' => 'Sports', 'International' => 'International'), null, ['placeholder' => 'Select Category']) !!}</td>
</tr>
<tr>
<td>{!! Form::label('author', 'Author', ['class' => 'control-label']) !!}</td>
<td>{!! Form::select('author', array('John' => 'John', 'Tom' => 'Tom', 'Jack' => 'Jack'), null, ['placeholder' => 'Select Author']) !!}</td>
</tr>
<tr>
<td valign="top">{!! Form::label('short_description', 'Short Description', ['class' => 'control-label']) !!}</td>
<td>{!! Form::textarea('short_description', null, ['class' => 'form-control']) !!}</td>
</tr>
<tr>
<td valign="top">{!! Form::label('full_content', 'Full Content', ['class' => 'control-label']) !!}</td>
<td>{!! Form::textarea('full_content', null, ['class' => 'form-control']) !!}</td>
</tr>
<tr>
<td></td>
<td>{!! Form::submit('Submit', ['class' => 'btn btn-submit']) !!}</td>
</tr>
</table>
{!! Form::close() !!}
@endsection
Here’s the template to show individual news. The URL will be http://your-site/news/{slug}</strong>
. This calls <strong>show($slug)</strong>
function of <strong>NewsController
class.
laravel-root/resources/views/news/show.blade.php
@extends('layouts.master')
@section('title', $news->title)
@section('sidebar')
@parent
// you can add something here
@endsection
@section('content')
<h1>{{ $news->title }}</h1>
Category: {{ $news->category }} | Author: {{ $news->author }} | Published on: {{ $news->created_at }}
{{ $news->full_content }}
@endsection
Here’s the template to edit news. The edit news form post action goes to update()</strong>
function of <strong>NewsController
class.
laravel-root/resources/views/news/edit.blade.php
@extends('layouts.master')
@section('title', $title)
@section('sidebar')
@parent
// you can add something here
@endsection
@section('content')
<h1>{{ $title }}</h1>
{!! Form::model($news, [
'method' => 'PATCH',
'route' => ['news.update', $news->id]
]) !!}
<table>
<tr>
<td>{!! Form::label('title', 'Title', ['class' => 'control-label']) !!}</td>
<td>{!! Form::text('title', null, ['class' => 'form-control', 'size' => 64, ]) !!}</td>
</tr>
<tr>
<td>{!! Form::label('slug', 'Slug', ['class' => 'control-label']) !!}</td>
<td>{!! Form::text('slug', null, ['class' => 'form-control', 'size' => 64, ]) !!}</td>
</tr>
<tr>
<td>{!! Form::label('category', 'Category', ['class' => 'control-label']) !!}</td>
<td>{!! Form::select('category', array('Politics' => 'Politics', 'Sports' => 'Sports', 'International' => 'International'), null, ['placeholder' => 'Select Category']) !!}</td>
</tr>
<tr>
<td>{!! Form::label('author', 'Author', ['class' => 'control-label']) !!}</td>
<td>{!! Form::select('author', array('John' => 'John', 'Tom' => 'Tom', 'Jack' => 'Jack'), null, ['placeholder' => 'Select Author']) !!}</td>
</tr>
<tr>
<td valign="top">{!! Form::label('short_description', 'Short Description', ['class' => 'control-label']) !!}</td>
<td>{!! Form::textarea('short_description', null, ['class' => 'form-control']) !!}</td>
</tr>
<tr>
<td valign="top">{!! Form::label('full_content', 'Full Content', ['class' => 'control-label']) !!}</td>
<td>{!! Form::textarea('full_content', null, ['class' => 'form-control']) !!}</td>
</tr>
<tr>
<td></td>
<td>{!! Form::submit('Submit', ['class' => 'btn btn-submit']) !!}</td>
</tr>
</table>
{!! Form::close() !!}
@endsection
Download Source Code from GitHub
Hope this helps. Thanks.