Laravel Tutorials - How to create Step by Step Laravel Project

How to create Step by Step Laravel Project

How to create Step by Step Laravel Project featured image
If you are a beginner and want to learn how to start developing Laravel applications you came to the right place because today we are going to learn step-by-step how to create your first Laravel 5.6 project. You are going to learn:

If you want to look at source code you can do it here

How to create your first Laravel project

Laravel framework has a few system requirements which are necessary to develop your applications smoothly. If you are not comfortable with installing different packages I would highly recommend you to use Laravel Homestead virtual machine which does all the job for you and makes your local development easy. 
 
However, if you are not using Homestead, you will need to make sure your server meets the following requirements:
  • PHP >= 7.1.3
  • OpenSSL PHP Extension
  • PDO PHP Extension
  • Mbstring PHP Extension
  • Tokenizer PHP Extension
  • XML PHP Extension
  • Ctype PHP Extension
  • JSON PHP Extension
Laravel utilizes Composer to manage its dependencies. So, before using Laravel make sure you have Composer installed on your machine.
 
First, you will need to download Laravel installer using Composer. In your terminal run this command:
$ composer global require "laravel/installer"
Make sure to place composer's system-wide vendor bin directory in your $PATH so the Laravel executable can be located by your system. This directory exists in different locations based on your operating system; however, some common locations include:
  • macOS: $HOME/.composer/vendor/bin
  • GNU / Linux Distributions: $HOME/.config/composer/vendor/bin
Once installed, you now can create your fresh Laravel installation:
$ laravel new blog
The word blog specifies in which directory to install Laravel project. The alternative method to install Laravel project would be using Composer command:
$ composer create-project --prefer-dist laravel/laravel blog
If you have PHP installed locally and would like to use built-in development server to serve your application you can use php artisan serve command which will make your server available at http://localhost:8000
 
The next thing you should do after installing your project is set your application key to a random string. You can do that by writing this command in terminal:
$ php artisan key:generate
If you wish to configure your database you can open .env file in your project's root directory and add your database credentials:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=secret
You should now be ready to build your first Laravel application. For this example, we will create a simple blog where you can create, view, edit and delete posts.

How to create posts

First of all, we need to create a Post model. This model will represent our database table and will be used to communicate with it. To simply create a model in Laravel we can write this in terminal:
$ php artisan make:model Post -m
The option -m or --migration is set to also create a migration file which we can find in database/migrations folder. We can open this newly created file and specify our table's columns:
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->text('description');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
After we save the file we can now migrate everything to the database:
$ php artisan migrate
We can now create a post controller and specify some functions:
$ php artisan make:controller PostController -r
The -r option saves us a little bit of time and creates basic CRUD functions. If you don't know what CRUD is it is Create, Read, Update and Delete functionality. We can now go to our app/Http/Controller/PostController.php file and create index function and return the view:
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
      return view('post.index');
    }
...
We can define our view in resources/views folder. In this case, we will rename welcome.blade.php file into index.blade.php and we will create a new folder called post and move index.blade.php file into this folder. So now you should have path like this resources/views/post/index.blade.php. Now we will change the layout a little bit:
<!doctype html>
<html lang="{{ app()->getLocale() }}">
  <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1">

      <title>{{ config('app.name') }}</title>

      <!-- Fonts -->
      <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet" type="text/css">

      <link rel="stylesheet" href="{{ asset('css/app.css') }}">
  </head>

  <body>
      <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
          <a class="navbar-brand" href="{{ route('post.index') }}">{{ config('app.name') }}</a>
          <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
              aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
              <span class="navbar-toggler-icon"></span>
          </button>

          <div class="collapse navbar-collapse" id="navbarSupportedContent">
              <ul class="navbar-nav mr-auto">
                  <li class="nav-item active">
                      <a class="nav-link" href="{{ route('post.index') }}">Home <span class="sr-only">(current)</span></a>
                  </li>
              </ul>

              <a href="{{ route('post.create') }}" class="btn btn-success my-2 my-sm-0">Create Post</a>
          </div>
      </nav>
      <div class="container py-3">
          <div class="row">
              <div class="col-md-4">
                  <div class="card">
                      <div class="card-header">
                          <h3>Post Title</h3>
                      </div>
                      <div class="card-body">
                          <p>Post Description</p>
                          <a href="#" class="btn btn-primary btn-block">Read More</a>
                      </div>
                  </div>
              </div>
          </div>
      </div>
  </body>

</html>
Lastly, we need to define routes for our post views. We can do that by editing routes/web.php file and adding:
Route::resource('post', 'PostController');
We specified a URL name we want to use and a controller. Now when we visit http://localhost:8000/post we can see our newly created view. What Route::resource() does it creates all the routes we need so if you would run in your terminal:
$ php artisan route:list

+--------+-----------+------------------+--------------+---------------------------------------------+--------------+
| Domain | Method    | URI              | Name         | Action                                      | Middleware   |
+--------+-----------+------------------+--------------+---------------------------------------------+--------------+
|        | GET|HEAD  | api/user         |              | Closure                                     | api,auth:api |
|        | GET|HEAD  | post             | post.index   | App\Http\Controllers\PostController@index   | web          |
|        | POST      | post             | post.store   | App\Http\Controllers\PostController@store   | web          |
|        | GET|HEAD  | post/create      | post.create  | App\Http\Controllers\PostController@create  | web          |
|        | GET|HEAD  | post/{post}      | post.show    | App\Http\Controllers\PostController@show    | web          |
|        | PUT|PATCH | post/{post}      | post.update  | App\Http\Controllers\PostController@update  | web          |
|        | DELETE    | post/{post}      | post.destroy | App\Http\Controllers\PostController@destroy | web          |
|        | GET|HEAD  | post/{post}/edit | post.edit    | App\Http\Controllers\PostController@edit    | web          |
+--------+-----------+------------------+--------------+---------------------------------------------+--------------+
You would see that there are routes with all the necessary methods and controller actions defined.
 
Let's now create our first dynamic post. First of all, we will create a new controller function create in our post controller:
...
public function create()
{
    return view('post.create');
}
...
Now we need to create our view. In our resources/view/post folder we will create create.blade.php file. We will set up a simple form which we will use to store our post's data:
<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>{{ config('app.name') }}</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet" type="text/css">

        <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    </head>

    <body>
        <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
            <a class="navbar-brand" href="#">{{ config('app.name') }}</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
                aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item active">
                        <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
                    </li>
                </ul>

                <a href="{{ route('post.create') }}" class="btn btn-success my-2 my-sm-0">Create Post</a>
            </div>
        </nav>
        <div class="container py-3">
            <div class="row">
                <div class="col-md-8 offset-md-2">
                    <form method="POST" action="{{ route('post.store') }}">
                        @csrf

                        <div class="form-group">
                            <label for="title">Title</label>
                            <input type="text" name="title" class="form-control" value="{{ old('title') }}"
                                placeholder="Post title">
                        </div>

                        <div class="form-group">
                            <label for="description">Description</label>
                            <textarea name="description" rows="8" cols="80"
                                class="form-control">{{ old('description') }}</textarea>
                        </div>

                        <button type="submit" class="btn btn-primary">Create Post</button>
                    </form>
                </div>
            </div>
        </div>
    </body>
</html>
You can see that I already wrote an action URL for form. You can also see that right after form there is a line with @csrf code. This is Blade syntax and it creates a hidden token field which is required to submit forms in Laravel. You can also see that in the input fields value attribute there is {{ old('title') }}. It is used when the form is submitted and rejected it puts the old form values so that we wouldn't need to enter all the values again.
 
We need to write our store function in PostController.php file:
...
public function store(Request $request)
{
    $validatedData = $this->validate($request, [
        'title'         => 'required|min:3|max:255',
        'description'   => 'required|min:3'
    ]);

    Post::create($validatedData);

    return redirect()->route('post.index');
}
...
First of all, we validate the data from our form. Then we use create method to save a new Post model in one line. However, before doing so, you will need to specify either a fillable or guarded attribute on the model, as all Eloquent models protect against mass-assignment by default. So let's go to our app/Post.php file and inside lets add:
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = ['title', 'description'];
}
 
Getting back to our controllers store() method, in the end, we just redirect the user to the main page. And also don't forget to include Post model into controller:
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Post;

class PostController extends Controller
{
...
If you did everything correctly you now should be able to create a post. Go to your post create page, fill out the form and submit it. If the page redirects to the main one it means you probably saved your post into the database. Now we need to display all the posts on the index page. We can do that in PostController.php index function:
...
public function index()
{
    $posts = Post::all();

    return view('post.index')->withPosts($posts);
}
...
We take all the table posts entries and return them to the main index view. In our index.blade.php file we need to create a foreach loop to display all of our records:
<!doctype html>
<html lang="{{ app()->getLocale() }}">
  <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1">

      <title>{{ config('app.name') }}</title>

      <!-- Fonts -->
      <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet" type="text/css">

      <link rel="stylesheet" href="{{ asset('css/app.css') }}">
  </head>

  <body>
      <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
          <a class="navbar-brand" href="{{ route('post.index') }}">{{ config('app.name') }}</a>
          <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
              aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
              <span class="navbar-toggler-icon"></span>
          </button>

          <div class="collapse navbar-collapse" id="navbarSupportedContent">
              <ul class="navbar-nav mr-auto">
                  <li class="nav-item active">
                      <a class="nav-link" href="{{ route('post.index') }}">Home <span class="sr-only">(current)</span></a>
                  </li>
              </ul>

              <a href="{{ route('post.create') }}" class="btn btn-success my-2 my-sm-0">Create Post</a>
          </div>
      </nav>
      <div class="container py-3">
          <div class="row">
              @foreach($posts as $post)
              <div class="col-md-4">
                  <div class="card">
                      <div class="card-header">
                          <h3>{{ $post->title }}</h3>
                      </div>
                      <div class="card-body">
                          <p>{{ substr($post->description, 0, 100) }}</p>
                          <a href="{{ route('post.show', $post->id) }}" class="btn btn-primary btn-block">Read More</a>
                      </div>
                  </div>
              </div>
              @endforeach
          </div>
      </div>
  </body>

</html>
We used Blade syntax for foreach loop and we passed $posts variable and defined a single object as $post. Now all the content inside foreach loop will continue until all the $posts objects will be passed. And we can access object’s data like that $post->title. For the description we used php function substr() which defines how many characters are displayed. And we also defined a 'Read More' button which we will link to our post.show view. For a post.show route, we need to pass post id which will tell the controller what post to show. Alternatively, you could pass a post slug to let the controller know which object to show. To find out more about the slugs you can read my other tutorial on How to Create Slugs in Laravel.
 
After we have done this, we need to find this post in a database and return it to the post.show view in our post controller’s function show:
...
public function show(Post $post)
{
    return view('post.show')->withPost($post);
}
...
And finally, we can create our new view in resources/views/post called show.blade.php:
<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>{{ config('app.name') }}</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet" type="text/css">

        <link rel="stylesheet" href="{{ asset('css/app.css') }}">
    </head>

    <body>
        <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
            <a class="navbar-brand" href="{{ route('post.index') }}">{{ config('app.name') }}</a>
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
                aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navbarSupportedContent">
                <ul class="navbar-nav mr-auto">
                    <li class="nav-item active">
                        <a class="nav-link" href="{{ route('post.index') }}">Home <span class="sr-only">(current)</span></a>
                    </li>
                </ul>

                <a href="{{ route('post.create') }}" class="btn btn-success my-2 my-sm-0">Create Post</a>
            </div>
        </nav>
        <div class="container py-3">
            <div class="row">
                <div class="col-md-8 offset-md-2">
                    <div class="card">
                        <div class="card-header">
                            <h1>{{ $post->title }}</h1>
                        </div>

                        <div class="card-body">
                            <p>{{ $post->description }}</p>

                            <a href="{{ route('post.edit', $post->id) }}" class="btn btn-primary">Edit Post</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>

</html>
We can see that now we don’t use foreach loop because we only have one object instead of a collection of objects. We can display our post information just like that {{ $post->title }}.

How to edit posts

You probably noticed that I already created an 'Edit Post' button with a new route. This route will be used to edit our post. Same as for 'Read More' button we need to pass an id of our post to the route.
 
Now, in our controller we can return post.edit view with post object:
...
public function edit(Post $post)
{
    return view('post.edit')->withPost($post);
}
...
We can actually duplicate the create.blade.php file and rename it to edit.blade.php. We only need to change a few things:
...
<div class="card">
    <div class="card-header">
        <h1>Edit Post</h1>
    </div>

    <div class="card-body">
        <form method="POST" action="{{ route('post.update', $post->id) }}">
            @csrf
            @method('PUT')

            <div class="form-group">
                <label for="title">Title</label>
                <input type="text" name="title" class="form-control" value="{{ $post->title }}">
            </div>

            <div class="form-group">
                <label for="description">Description</label>
                <textarea name="description" rows="8" cols="80" class="form-control">{{ $post->description }}</textarea>
            </div>

            <button type="submit" class="btn btn-primary">Update Post</button>
        </form>
    </div>
</div>
...
We changed the form action route and instead of {{ old(‘title’) }} we can write {{ $post->title) }} to display data in the fields. Another thing to keep in mind that route post.update uses PUT method so to make it work we also need to put @method('PUT') which similarly as @csrf makes a hidden field with method value so that Laravel would allow us to submit this form.
 
Now we can go back to our controller file and for the update function put:
...
public function update(Request $request, Post $post)
{
      $validatedData = $this->validate($request, [
        'title'         => 'required|min:3|max:255',
        'description'   => 'required|min:3'
      ]);

      $post->update($validatedData);

      return redirect()->route('post.show', $post);
}
...
You can see that this method is almost the same as for store function. The only difference is that we don’t need to create the new Post instance, we need to find an existing object in our database and just update it.
 
If you did everything correctly, you should now be able to update your post. The last thing we need to cover is deleting the post.

How to delete posts

In the show.blade.php file we can add an additional button named 'Delete' with a new route. The difference between 'Edit Post' button is that we cannot just simply pass a route we need to submit it via form and define DELETE method:
...
<div class="card-body">
    <p>{{ $post->description }}</p>

    <a href="{{ route('post.edit', $post->id) }}" class="btn btn-primary">Edit Post</a>
    <form action="{{ route('post.destroy', $post->id) }}" method="post">
        @csrf
        @method('DELETE')
        <button type="submit" class="btn btn-danger mt-3">Delete</button>
    </form>
</div>
...
 
And lastly, we need to write a logic for a destroy function inside our PostController.php file:
...
public function destroy(Post $post)
{
      $post->delete();

      return redirect()->route('post.index');
}
...
And that’s it! You now should be able to delete your posts.
 
Congratulations! You now have built your first Laravel application. I know it can be a little bit overwhelming but once you get it you could actually see that a lot of things repeat itself. You create a route, then define what that route does in your controller file and then you create a view to display your content. If you wish to see the source code you can do it here. After you clone repository in your terminal you can run this command to get to the end of this tutorial:
$ git checkout create-project -b <your-branch-name>
 
If you had any problems or just simply have questions feel free to comment down below.
How to create Step by Step Laravel Project pinterest image

Related Tutorials

How to Generate a Simple XML Sitemap using Laravel

Posted 1 year ago

Today we are going to learn how to generate a simple XML sitemap using Laravel. Mainly you can create it either manually or create some sort of automated solution. To create it manually you can make y...

Read More

How to Create Slugs in Laravel

Posted 1 year ago

Today we are going to learn how to create slugs in Laravel. You are going to learn: Why use slugs How to create slugs How to show objects with the slugs How to edit slugs Why use Slugs If yo...

Read More
Back