In this post, we start building a Laravel 8 Application just created. You can follow the tutorial Create a Laravel 8 application from scratch to install and create your first project and the initial application structure. However, this can be applied to a project created using the Laravel Sail tools or Composer.
The main areas to work at the start of the project are:
- Understanding the project directory structure
- Navigation paths, adding/modifying routes
- Application screens, creating/modifying views
Laravel 8 Project Directory Structure
The main folders used by a laravel 8 project are:
- app: contains the application code. It includes Controllers, Middleware, Models, and Providers
- bootstrap: includes the
app.php
file (main Application object). It contains thecache
directory, used to cache generated files to improve performance. - config: contains all the configuration files, including app localization, timezone, application components, databases, filesystems, logs.
- database: includes database scripts for schema definitions, migrations, and factories. You can use this directory to store an SQLite database.
- public: all the public/static files to be served by the application, including the main page script (index.php), icons, and other compiled assets.
- resources: contains the application views (PHP blade templates) as well as any un-compiled assets such as CSS or JavaScript. This directory also stores application language files for internationalization.
- routes: this is the place to define routes for your application, console commands, REST APIs, and broadcasts.
- tests: contains PHPUnit automated tests created to verify your application components, and generated reports of the test results.
- vendor: this folder automatically stores all Laravel and third-party Composer components and the autoloader scripts.
A plain view of the main directory tree looks like this:
+-app |---Console |---Exceptions |---Http |---Models |---Providers +-bootstrap |---cache +-config +-database |---factories |---migrations |---seeders +-public +-resources |---css |---js |---lang |---views +-routes +-storage |---app |---framework |---logs +-tests |---Feature |---Unit +-vendor
Laravel 8 application routes and views
The first step is to learn about the Laravel routing configuration. Routing is the mechanism to map a URL path to a specific resource of the application. The main configuration files for routes are stored in the /routes/ folder:
web.php
: routes for the web application.api.php
: routes for a REST API interfaceconsole.php
: commands to run usingartisan
console commandschannels.php
: used to “broadcast” your server-side Laravel events over a WebSocket connection (see https://laravel.com/docs/8.x/broadcasting)
Web routing
The main application route configuration is stored at routes/web.php
. This file already contains the main route (the root path /
) pointing to a welcome page view.
Route::get('/', function () {
return view('welcome');
});
Views are stored in the /resources/views/
folder. Normally, you can use blade templates to work with PHP views adding additional templating features like value formatting, loops, conditions, sub-views. In this example calling to view(‘welcome’) will route the root path to the file /resources/views/welcome.blade.php
(see more details in the views section)
Each route has a method, a URI expression and the request callback to manage requests to this route.
Route methods
A route can be defined using any common HTTP method, matching the Route class method name:
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
Also, you can use Route::match()
to match more than one method, and Route::any()
to match any method.
Route::match(['get', 'post'], '/', function () {
// controller code for both GET and POST requests
});
Route::any('/', function () {
// controller code for any request method
});
Route URIs
The $uri
parameter is a literal path or a path expression containing parameters surrounded by brackets (eg: {parameter}
).
// literal route, exact match
Route::post('/register', function (Request $request) {
return view('register');
});
// parametrized route , example: /notification/1234
Route::get('/notification/{id}', function ($id) {
return view('notification',['notification_id' => $id]);
});
Route callbacks
Callbacks could be inline functions, like in the previous examples, or calls to Controller methods:
use App\Http\Controllers\LoginController;
Route::post('/login', [LoginController::class,'authenticate']);
In some cases, the controller class could be defined dynamically, using the full namespace:
Route::post('/login', ['uses'=>'App\\Http\\Controllers\\LoginController@authenticate']);
Route Controllers
Controllers can be defined in the app/Http/Controllers
directory, using the name of the controller class as the filename (LoginController.php
):
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/**
* Handle an authentication attempt.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function authenticate(Request $request)
{
// user authentication
}
}
Requests and responses
Finally, you can use the Request
object (passed to your controller function before any route parameter) to get information and available methods to manage the $request and the final response(). For example:
Route::patch('/user/{id}', function(Request $request, $id){
$user = User::findOrFail($id);
$user->fill($request->only(['firstName','lastName']));
$user->save();
if ($request->expectsJson()){
return response()->json([
'status' => 'success'
]);
}else{
redirect('/user/'.$id);
}
}
This code will retrieve the user $id
from the route path ({id}
) and specific data from the user using $request->only([keys])
, in order to replace this information in the user model data (calling $user->fill()
and $user->save()
). Additionally, it will return a JSON response if the request expects JSON (from an AJAX-type request, for example) or simply redirect to a specific URL path.
By default, Laravel will return the content using the text/html
mime-type. If you want to respond with a different response code or adding headers, you can use the response(content, status)
function:
return response('Text content', 200)
->header('Content-Type', 'text/plain');
You can redirect your response using redirect('/path')
. Also, you can redirect the response to a specific Controller/method, also passing parameters:
return redirect()->action(
[PaymentController::class, 'thankyou'], ['transaction_id' => $id]
);
One common scenario is to process data and the return a view (using a Laravel blade template file or a plain PHP view):
Route::get('/terms-and-conditions', function () {
return view('terms');
});
In the next section, we explain how views works.
Laravel 8 application views
By default, Laravel views are built using the Blade template language, and stored in the /resources/views/
folder. The Blade templating language is a mix of literal HTML code, directives starting with @
(example: @include
, @if
and @foreach
) and PHP expressions surrounded by double brackets, for example:
@if( count($users)>0 )
<span >{{ count($users) }} users</span>
@endisset
To differentiate plain PHP views from blade templates, use the .blade.php
suffix to process a file as a blade template.
Displaying data
Blade uses the double curly brackets ( {{ expression }}
) to echo information to the PHP output. By default, all the content generated by blade expressions is automatically encoded/escaped to prevent XSS attacks (using raw HTML to break your code). You can use any valid PHP variable, expression, or function call as an expression:
<div>Number of items: {{ count($items) }}.</div>
Displaying raw HTML
Sometimes you may want to display HTML generated code in your template (warning: do this only from a trusted generated code, not directly from the user input), in this case, use the double exclamation mark between curly brackets ( {!!
expression !!}
) to display raw HTML
{!! $component->render() !!}
Displaying JSON data
You may need to embed JSON code inside your template. When you need some javascript code to process JSON data use @json($data). Optionally, you can add the JSON_PRETTY_PRINT
option to format the JSON output:
<script>
var appdata = @json($appdata, JSON_PRETTY_PRINT);
var app = new Vue({
el: '#app',
data: appdata
})
</script>
The expression will be converted to a JSON-compatible expression like:var appdata = { "id" : 1241, "name" : "Product 1"}
;
Blade Directives
Laravel Blade directives help to build complex views using programmatic logic found in most programming languages. The most common directives are:
Conditional @if directive
Is the common way to conditionally display a block based on conditions:
@if (count($results) === 1)
<span>1 result</span>
@elseif (count($results) > 1)
<span>{{ count($results) }} results</span>
@else
<span>No results</span>
@endif
@isset and @empty directives
@isset
checks if a variable is defined and not null. On the other hand, @empty
checks if the variable has an ’empty’ value: null
, false
, ''
, []
empty array and others (see empty()
)
@isset($errorMessage)
<span class='error-message'>{{ $errorMessage }}</span>
@endisset
@empty($records)
<span>No matches found</span>
@endempty
Loop directives
You can loop over data using @for
@foreach
@forelse
and @while
// regular for
@for ($i = 0; $i < count($results); $i++)
<div>Name: {{ $results[$i]["firstName"] }}</div>
@endfor
// for each element of a list
@foreach ($results as $result)
<div>Name: {{ $result["firstName"] }}</div>
@endforeach
// for each element, fallback if empty
@forelse ($results as $result)
<div>{{ $result["firstName"] }}</div>
@empty
<div>No results</div>
@endforelse
// loop while a condition is true
@while ($dataSource->hasData())
<div>{{ $dataSource->data['id'] }}</div>
@endwhile
As in regular loops, you can use the @break
to stop the loop, and @continue
directive to skip/pass the current loop. Also, you can check the value of $loop->index
(0-based) attribute to get the current loop index or $loop->iteration (1-based) to get the current loop count (see more options in the Laravel Blade documentation).
@foreach ($users as $user)
@if ($user->isDeleted())
@continue
@endif
<li>{{ $user->name }}</li>
@if ($loop->iteration == 10)
@break
@endif
@endforeach
Adding sub-views using @include
You can include other sub-views inside a blade template using @include( view, parameters)
. For example, you can create your views using a mix of local content and reuse of common components (the same header/footer for each page, for example):
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
@include('head',['title'=>'My Page'))
<body>
@include('components/toolbar')
<div id="content">
My content ...
</div>
@include('components/footer')
</body>
</html>
This structure will use a sub-view stored in /resources/views/head.blade.php
to fill the <head></head>
section and 2 components to create the header/toolbar and the footer of the application (both created at /resources/views/components/
). However, all relative paths when calling @include will be relative to /resources/views/
.
Going further with views
Laravel also provides a way to reuse a common page structure using Layouts. This will be covered in a different post. Stay tuned!