Tutorial Series
Laravel is an open-source PHP framework that provides a set of tools and resources to build modern PHP applications. In this project-based tutorial series, you’ll build a Links Landing Page application with the Laravel framework, using a containerized PHP development environment managed by Docker Compose.
At the end, you’ll have a one-page website built with Laravel and managed via Artisan commands where you can share relevant links to an audience on social channels and presentations.
To follow this series, you will need:
Once you have these prerequisites set up, you are ready to get started.
Tutorial
Updated on April 29, 2022
To get started, you’ll need to create a containerized environment able to execute PHP and Composer, the PHP dependency management tool. Then, you’ll be able to bootstrap the new Laravel application from scratch, without the need to have a local PHP environment installed on your local machine or development server.
In this guide, streamlined instructions will be provided on how to set this environment up based on our tutorial on How To Install Laravel with Docker Compose on Ubuntu 20.04. Please refer to that tutorial for more detailed instructions on each of the options used within the Docker Compose file that will be provided in this guide.
Create a new directory for your application in your home folder:
- mkdir ~/landing-laravel
- cd ~/landing-laravel
Next, you’ll create the docker-compose.yml
file that will define the containerized environment. In this file, you’ll set up a service named app
, which will be based on a custom Docker image built with a Dockerfile you’ll set up later on.
The build arguments user
and uid
, both defined in the docker-compose.yml
file and used in the Dockerfile at build time, should be changed to reflect your own username and uid on your local machine or development server. To find out your current user’s uid, type:
- echo $UID
Output1000
The user
and uid
variables will be available at build time and will be used in the Dockerfile to create a new user in the app
service with the same username and uid as your current system user on your local machine or development server. This will avoid permission and ownership issues when working with application files both from the container as well as from the host that executes Docker.
Create a new docker-compose.yml
file using your text editor of choice. Here, nano
is used:
- nano docker-compose.yml
Copy the following content to this file, and don’t forget to replace the highlighted values with appropriate values depending on your own username and uid on the system that runs Docker:
version: "3.7"
services:
app:
build:
args:
user: sammy
uid: 1000
context: ./
dockerfile: Dockerfile
image: landing-app
restart: unless-stopped
working_dir: /var/www/
volumes:
- ./:/var/www
networks:
- landing
networks:
landing:
driver: bridge
Save and close the file when you are done. If you are using nano
, you can do that by pressing CTRL
+X
, then Y
and ENTER
to confirm.
Next, you’ll set up the Dockerfile that is referenced in the docker-compose.yml
file, which will set up a custom image for the app
service:
- nano Dockerfile
This Dockerfile extends from the default php:7.4-fpm
Docker image. It uses the user
and uid
variables to create a new user able to execute Artisan and Composer commands. It also installs a few PHP dependencies that are required by Laravel, and the Composer executable.
Copy the following content to your Dockerfile:
FROM php:7.4-fpm
# Arguments defined in docker-compose.yml
ARG user
ARG uid
# Install system dependencies
RUN apt-get update && apt-get install -y \
git \
curl \
libpng-dev \
libonig-dev \
libxml2-dev \
zip \
unzip
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
# Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
chown -R $user:$user /home/$user
# Set working directory
WORKDIR /var/www
USER $user
Save and close the file when you’re done. Next, you can bring your environment up with:
- docker-compose up -d
This command will execute Docker Compose in detached mode, which means it will run in the background. The first time you bring an environment up with a custom image, Docker Compose will automatically build the image for you before creating the required containers. This might take a few moments to finish. You’ll see output similar to this:
OutputCreating network "landing-laravel_landing" with driver "bridge"
Building app
Step 1/11 : FROM php:7.4-fpm
---> fa37bd6db22a
...
Step 10/11 : WORKDIR /var/www
---> Using cache
---> 769afd5d44d8
Step 11/11 : USER $user
---> Using cache
---> 841eb5852b69
Successfully built 841eb5852b69
Successfully tagged landing-app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Creating landing-laravel_app_1 ... done
You can verify that your environment is up and running with:
- docker-compose ps
Output Name Command State Ports
------------------------------------------------------------------------
landing-laravel_app_1 docker-php-entrypoint php-fpm Up 9000/tcp
Once the app
service is up, you can run Composer, the PHP dependency management tool, to bootstrap a new Laravel application. In order to do that, you’ll use docker compose exec
to run commands on the app
service, where PHP is installed.
The following command will use Docker Compose to execute composer create-project
, which will bootstrap a fresh installation of Laravel based on the laravel/laravel
package:
- docker-compose exec app composer create-project laravel/laravel --prefer-dist application
Creating a "laravel/laravel" project at "./application"
Installing laravel/laravel (v8.4.0)
- Downloading laravel/laravel (v8.4.0)
- Installing laravel/laravel (v8.4.0): Extracting archive
Created project in /var/www/application
> @php -r "file_exists('.env') || copy('.env.example', '.env');"
Loading composer repositories with package information
Updating dependencies
Lock file operations: 104 installs, 0 updates, 0 removals
…
Package manifest generated successfully.
71 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan key:generate --ansi
Application key set successfully.
This installation creates a new .env
file based on the default .env.example
file that comes with Laravel. The .env
file contains database credentials and other sensitive application settings, and should be unique per environment where the app runs. You’ll come back to edit this file after you finish setting up the development environment.
Next, copy the application files to the same directory as the docker-compose.yml
file, so that you can share Laravel’s environment variables file with Docker Compose. Then, you can remove the application
directory created by Composer:
- cp -rT application .
- rm -rfv application
Your application is now bootstrapped, but you’ll need to include a couple services in the Docker Compose file in order to be able to access the app from a browser. An nginx
service will serve the application using the Nginx web server, and a db
service will host the application’s MySQL database.
First, bring your environment down with:
- docker-compose down
OutputStopping landing-laravel_app_1 ... done
Removing landing-laravel_app_1 ... done
Removing network landing-laravel_landing
This will remove all containers and networks associated with this environment. Before editing your docker-compose.yml
file to add the new services, create a new directory to share configuration files with containers. You’ll need this to properly set up Nginx to handle the Laravel PHP application.
- mkdir -p docker-compose/nginx
Next, create a new landing-laravel.conf
file containing a custom Nginx server block. Later on, you’ll set up a volume to share this file within the nginx
service container.
Open a new Nginx configuration file with:
- nano docker-compose/nginx/landing-laravel.conf
The following server block configures Nginx to serve a Laravel application using an external service (app
) to handle PHP code. Copy this content to your own Nginx configuration file:
server {
listen 80;
index index.php index.html;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /var/www/public;
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
gzip_static on;
}
}
Save and close the file when you’re done.
Next, open your docker-compose.yml
file:
- nano docker-compose.yml
Include the following configuration for the nginx
service, at the same level as the previously configured app
service. This will create a new service based on the nginx:alpine
image, and all requests on port 8000
of the host where Docker is running will be redirected to port 80
in the service container. In addition to the application files, you’ll also share a volume containing Nginx’s configuration file for a Laravel application:
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- 8000:80
volumes:
- ./:/var/www
- ./docker-compose/nginx:/etc/nginx/conf.d/
networks:
- landing
Then, include the following configuration block for the db
service. This will create a service based on the default MySQL 8 image, and pull in the values defined in Laravel’s environment file to set up database access:
db:
image: mysql:8
restart: unless-stopped
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME}
networks:
- landing
This is how your updated docker-compose.yml
file should look like once you’re finished:
version: "3.7"
services:
app:
build:
args:
user: sammy
uid: 1000
context: ./
dockerfile: Dockerfile
image: landing-app
restart: unless-stopped
working_dir: /var/www/
volumes:
- ./:/var/www
networks:
- landing
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- 8000:80
volumes:
- ./:/var/www
- ./docker-compose/nginx:/etc/nginx/conf.d/
networks:
- landing
db:
image: mysql:8
restart: unless-stopped
environment:
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_USER: ${DB_USERNAME}
networks:
- landing
networks:
landing:
driver: bridge
Note: for more detailed information about containerizing Laravel environments, including explanations about shared volumes and networks, please refer to our full guide on How To Install Laravel with Docker Compose on Ubuntu 20.04.
Save and close the file when you’re done editing. Lastly, update your Laravel dot env file (.env
) to point the MySQL database host configuration to the host where the MySQL service will be running, called db
:
- nano .env
The .env
file that is automatically generated by Composer upon installation comes with some default values that you might want to change, such as the APP_NAME
and the APP_URL
. The database DB_HOST
variable must be changed to point to the service where MySQL will be running, and you can reference it by its service name, as defined in the docker-compose.yml
file. In this example, you’ve used db
as the name for the database service, so this will be available in the containerized network as a host named db
.
Change your .env
accordingly, using the following example as base. The highlighted values were updated here to reflect the state of the application under development:
APP_NAME=LandingLaravel
APP_ENV=local
APP_KEY=base64:ffYPNP8kPeQDf8gE/qh3kWjk59p6gFY66kCKhhKUa2w=
APP_DEBUG=true
APP_URL=http://localhost:8000
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=landing-db
DB_USERNAME=landing-user
DB_PASSWORD=dev-password
...
You don’t need to change any other sections of this file, but feel free to tweak to your specific use case.
Save and close the file when you’re done editing its contents.
You can now bring the updated environment up with:
- docker-compose up -d
OutputCreating network "landing-laravel_landing" with driver "bridge"
Creating landing-laravel_app_1 ... done
Creating landing-laravel_db_1 ... done
Creating landing-laravel_nginx_1 ... done
With the full environment up, you can now point your browser to localhost
or your remote server’s IP address, on port 8000
:
http://localhost:8000
If everything works as expected, you’ll see a page like this:
In the next part of this series, you’ll create a database migration to set up a links table.
Tutorial
Updated on April 29, 2022
Laravel database migrations allow developers to quickly bootstrap, destroy, and recreate an application’s database, without the need to log into the database console or run any SQL queries.
In this guide, you’ll create a database migration to set up the table where you’ll save the application links. In order to do that, you’ll use the Artisan command-line tool that comes with Laravel by default. At the end, you will be able to destroy and recreate your database tables as many times as you want, using only artisan
commands.
To get started, first make sure you’re in the application’s root directory and your Docker Compose development environment is up and running:
- cd ~/landing-laravel
- docker-compose up -d
Outputlanding-laravel_app_1 is up-to-date
landing-laravel_nginx_1 is up-to-date
landing-laravel_db_1 is up-to-date
Next, create a database migration to set up the links
table. Laravel Migrations allow developers to programmatically create, update, and destroy database tables, working as a version control system for your database schema.
To create a new migration, you can run the make:migration
Artisan command and that will bootstrap a new class on your Laravel application, in the database/migrations
folder. This class will contain a default boilerplate code.
Remember to use docker-compose exec app
to run the command on the app
service container, where PHP is installed:
- docker-compose exec app php artisan make:migration create_links_table
OutputCreated Migration: 2020_11_18_165241_create_links_table
Note: The migration name is generated based on the current date and time, and the name provided as an argument to the make:migration
command. For that reason, your migration file name will differ slightly. For the exact file name, check with the following:
- find ~/landing-laravel/database/migrations -name '*create_links_table.php'
Output/home/sammy/landing-laravel/database/migrations/2020_11_18_165241_create_links_table.php
Open the generated migration class using your editor of choice:
- nano database/migrations/2020_11_18_165241_create_links_table.php
Next, update the up
method to include the table columns you’ll need to store the app data.
Replace the current content of your migration class with the following code. The highlighted values are the only lines that need adding, so if you prefer, you can also only copy those highlighted lines and include them into your Schema::create
definition:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateLinksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('links', function (Blueprint $table) {
$table->id();
$table->string('url', 200);
$table->text('description');
$table->boolean('enabled')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('links');
}
}
In addition to the default fields that are included in the table definition that is automatically generated with the Artisan command, you’re including three new fields in this table:
url
: A string field to save the link URL.description
: A text field to save the link description.enabled
: A field to store the state of the link, whether it’s enabled or not. The boolean
Schema type will generate a tinyint
unsigned field to store a value of either 0
of 1
.Save your migration file when you’re done adding these fields. Next, run the migration with:
- docker-compose exec app php artisan migrate
OutputMigration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (152.46ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (131.12ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (101.06ms)
Migrating: 2020_11_18_165241_create_links_table
Migrated: 2020_11_18_165241_create_links_table (60.20ms)
You’ll notice that other migrations were also executed along with the create_links_table
. That is because the default Laravel installation comes with migrations for users (with a users
table and a password_resets
table) and for queued jobs (with a failed_jobs
table). Because your demo application won’t use these features, it is safe to remove those migrations now; however, you may also opt to leave them in place if you are working on an application of your own and you plan on developing it further. All migration files are located at database/migrations
in the app’s root folder.
For more detailed information on database migrations, please refer to our guide on How To Use Database Migrations and Seeders to Abstract Database Setup in Laravel.
In the next part of this series, you’ll create a custom Artisan command to list, insert, and delete entries in the app’s links table.
Tutorial
Updated on April 29, 2022
Eloquent is an object relational mapper (ORM) included by default within the Laravel framework. It facilitates the task of interacting with database tables, providing an object-oriented approach to inserting, updating, and deleting database records, while also providing a streamlined interface for executing SQL queries.
Eloquent uses database models to represent tables and relationships in supported databases. The name of the database table is typically inferred from the model name, in plural form. For instance, a model named Link
will use links
as its default table name.
You can use the artisan make:model
command line helper to generate new models for your application. To create a new Eloquent model for your links
table, run:
- docker-compose exec app php artisan make:model Link
OutputModel created successfully.
This will generate a new file containing a barebones model class. Even though this class has no apparent properties or methods, when operating the model via facades, you have access to the underlying Eloquent database classes that are able to identify database table structures and represent them as fully-functional objects.
For your reference, this is the automatically generated model class:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Link extends Model
{
use HasFactory;
}
For the purpose of this series, you don’t need to make any changes to this file. If you want to extend the application in the future, you might use this model to create custom methods for the Link
class that involve database operations. Additionally, if you want to create relationships between the Link
model and other models, you’ll need to include a method representing the relationship in at least one of the sides. For detailed information about Eloquent relationships, please refer to the official documentation.
In the next part of this series, you’ll create Artisan commands that will use this model to select, insert, and delete links on your database.
Tutorial
Updated on April 29, 2022
If you followed along with this series so far, your database tables should be all set. However, you still need to implement a way to let users insert new entries in the links
table.
To limit the scope of this series while also making the application fully-functional, you’ll set up Artisan commands to create and delete links in the database. Artisan is the command line tool that comes with Laravel, offering a number of utilities to speed up the development process, from generating boilerplate code to deleting and re-creating the application’s database.
Using the command line interface to manage your application can be an alternative to web forms and secured areas, since it requires a user to be logged on the server in order to execute such commands instead of being authenticated from a browser. If you decide later on to create a secured area for your application, you can create web forms to allow a registered user to submit a new link to the database.
Artisan commands are often used to perform application tasks that should run in the background, either manually or automatically via a scheduling mechanism such as Crontab. They can also be used to facilitate prototyping new application features that need to be configured dynamically, depending on input from an authorized user.
To get started, create a new Artisan command using the make:command
helper:
- docker-compose exec app php artisan make:command LinkNew
OutputConsole command created successfully.
This will create a file named LinkNew.php
, located at the app/Console/Commands
directory. In this class, which extends from the Illuminate\Console\Command
parent class, you’ll need to implement a handle
method that will be executed when this command is called. To define the signature of the command, you’ll set the $signature
protected property to link:new
.
Open the new file using your text or code editor of choice. Here, nano
is used:
- nano app/Console/Commands/LinkNew.php
A few different things will need to happen in the handle
method so that you are able to save a new link to the database. First, you’ll prompt for the user’s input in order to obtain the link URL.
$url = $this->ask('Link URL:');
Then, you’ll use the filter_var
function to validate that the input obtained from the user is a valid URL. If the link is invalid, you’ll show an error and exit the application with status code 1
, which means the application exited in error.
if (!filter_var($url, FILTER_VALIDATE_URL)) {
$this->error("Invalid URL. Exiting...");
return 1;
}
If the link is valid, you’ll continue and ask for the link description using the same method as before.
$description = $this->ask('Link Description:');
You’ll then ask for a final confirmation that all data is correct, using the confirm helper. If the user confirms, the link is finally inserted in the database. You’ll use the Link
Eloquent model created in a previous part of this series to interact with the database.
$this->info("New Link:");
$this->info($url . ' - ' . $description);
if ($this->confirm('Is this information correct?')) {
$link = new Link();
$link->url = $url;
$link->description = $description;
$link->save();
$this->info("Saved.");
}
The application exits with a 0
, representing a success status (0 errors).
return 0;
The following code contains the full implementation of these steps. Replace the current content in your LinkNew
class with:
<?php
namespace App\Console\Commands;
use App\Models\Link;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class LinkNew extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'link:new';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a New Link';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$url = $this->ask('Link URL:');
if (!filter_var($url, FILTER_VALIDATE_URL)) {
$this->error("Invalid URL. Exiting...");
return 1;
}
$description = $this->ask('Link Description:');
$this->info("New Link:");
$this->info($url . ' - ' . $description);
if ($this->confirm('Is this information correct?')) {
$link = new Link();
$link->url = $url;
$link->description = $description;
$link->save();
$this->info("Saved.");
}
return 0;
}
}
Save and close the file when you’re done.
To execute the command and insert a new link in the database, run:
- docker-compose exec app php artisan link:new
Output Link URL::
> https://digitalocean.com/community
Link Description::
> DigitalOcean Community
New Link:
https://digitalocean.com/community - DigitalOcean Community
Is this information correct? (yes/no) [no]:
> yes
Saved.
Feel free to add a few more links if you want to.
Next, you’ll need to create a new Artisan command to show the list of all links.You can call it link:list
. Create the new command with:
- docker-compose exec app php artisan make:command LinkList
Open the command class using your text or code editor of choice:
- nano app/Console/Commands/LinkList.php
Within the handle
method of this command, you’ll query for all rows in the links
table. You can use the Link
model to access the underlying database query methods that Eloquent provides. To exhibit the results nicely in the command line, you can use the table output helper:
$headers = [ 'id', 'url', 'description' ];
$links = Link::all(['id', 'url', 'description'])->toArray();
$this->table($headers, $links);
return 0;
The following code contains the full implementation of the link:list
command. Replace the content in your LinkList.php
file with :
<?php
namespace App\Console\Commands;
use App\Models\Link;
use Illuminate\Console\Command;
class LinkList extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'link:list';
/**
* The console command description.
*
* @var string
*/
protected $description = 'List links saved in the database';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$headers = [ 'id', 'url', 'description' ];
$links = Link::all(['id', 'url', 'description'])->toArray();
$this->table($headers, $links);
return 0;
}
}
Save and close the file when you are done.
To run this command and show a list of all links already inserted in the links
table, run:
- docker-compose exec app php artisan link:list
Output+----+------------------------------------+--------------+
| id | url | description |
+----+------------------------------------+--------------+
| 1 | https://digitalocean.com/community | DO Community |
| 2 | https://laravel.com | Laravel |
+----+------------------------------------+--------------+
Finally, you’ll create a command to delete links:
- docker-compose exec app php artisan make:command LinkDelete
OutputConsole command created successfully.
Open the new file using your text or code editor of choice:
- nano app/Console/Commands/LinkDelete.php
You can name this command link:delete
. To know which link must be deleted, you’ll need to require that users provide an additional argument when calling the command: the ID of the link. This is also set within the $signature
variable, which defines how your command is called and what arguments, mandatory or not, should be provided:
protected $signature = 'link:delete {link_id}';
The handle
method for this command will implement a few different instructions. First, you’ll obtain the Link ID that should have been provided within the command call.
$link_id = $this->argument('link_id');
Then, you’ll obtain the referenced link from the database, using the Eloquent method find
that is available through your Link
model.
$link = Link::find($link_id);
When the find
method doesn’t find a database record with that ID, it will return null
. You’ll check if that is the current value contained in the $link
variable, and return an error in that case. The program will exit in error (code 1).
if ($link === null) {
$this->error("Invalid or non-existent link ID.");
return 1;
}
When $link
is not null, the command continues execution. You then use the confirm
helper to ask for a user confirmation.
if ($this->confirm('Are you sure you want to delete this link? ' . $link->url)) {
// deletes link
}
When the user confirms the action by typing yes
and hitting ENTER
, you’ll call the delete
method from the Link
Eloquent model to delete the specified link from the database.
$link->delete();
$this->info("Link deleted.");
The following code contains the full implementation for the list:delete
command. Replace the content in your LinkDelete.php
file with the following:
<?php
namespace App\Console\Commands;
use App\Models\Link;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class LinkDelete extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'link:delete {link_id}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Deletes a link from the database.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$link_id = $this->argument('link_id');
$link = Link::find($link_id);
if ($link === null) {
$this->error("Invalid or non-existent link ID.");
return 1;
}
if ($this->confirm('Are you sure you want to delete this link? ' . $link->url)) {
$link->delete();
$this->info("Link deleted.");
}
return 0;
}
}
Save and close the file when you’re done.
Now when you want to delete a link from your links table, you’ll first need to obtain the link’s ID with artisan link:list
, as demonstrated earlier on. Once you know the ID of a link, you can run the artisan link:delete
command with:
- docker-compose exec app php artisan link:delete LINK_ID
Output
Are you sure you want to delete this link? https://laravel.com (yes/no) [no]:
> yes
Link deleted.
You’re now able to insert, list, and delete links in the application’s database, using Artisan commands executed from a command-line interface. In the next part of this series, you’ll set up the front end of your application using Blade templates and the Bulma CSS framework.
Tutorial
Published on December 17, 2020
So far, you’ve seen how to set up the application’s MySQL database tables using migrations, how to create an Eloquent model to interact with the links table, and how to create Artisan commands to manage links in the database. You’ll now see how to create a custom Blade template to show your links in the application’s front-end. To facilitate styling this page while keeping it minimal, for this series we are going to use Bulma, a single-file CSS framework.
The default route set up within the Laravel web routes file points to an example template that you can find at resources/views/welcome.blade.php
. You’ll create a new index.blade.php
file within that same directory, and edit the main routes file so that the /
route points to this template instead. In the route definition, you’ll also need to obtain a list of all links that you want to show in the new index template.
Start by updating the routes file of your Laravel application. Open the routes/web.php
file using your text or code editor of choice:
- nano routes/web.php
Your current /
route points to the example page that comes with Laravel by default:
Route::get('/', function () {
return view('welcome');
});
To make the proposed changes, first you’ll use the Link
Eloquent model to fetch all links from the database, and sort them in decreasing order to make sure any new links you create are listed first, and thus will be shown at the top of the page.
$links = Link::all()->sortDesc();
The view
helper function will look for a template file named welcome.blade.php
, in the root of the resources/views
directory, and return the rendered result to the browser. You’ll change this to point to a new index.blade.php
template. Additionally, you’ll pass the $links
variable along as template data.
return view('index', [
'links' => $links
]);
The following code implements the discussed changes for the /
route. Replace the contents in your routes/web.php
file with:
<?php
use Illuminate\Support\Facades\Route;
use App\Models\Link;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('index', [
'links' => Link::all()->sortDesc()
]);
});
Save and close the file when you’re done.
The routes file is all set, but if you try to access your main application’s page right now you will get an error message because the index.blade.php
template doesn’t exist yet. You’ll create it now.
You can base your template on the Bulma starter template, which provides a minimal HTML page structure with a title, a subtitle, and a main content area. Later on, you’ll include some CSS styling to customize the appearance of this page.
To get started, create a new index.blade.php
template using your text or code editor of choice:
- nano resources/views/index.blade.php
Apart from the HTML boilerplate code, which creates the page structure and the static elements that you may want to use (such as headers and other information), you’ll need to show the list of links that was passed along as template data —a collection of Link
objects.
You can use Blade’s foreach loop to loop through the links in the collection, and output them to the page:
@foreach ($links as $link)
<li>
<a href="{{ $link->url }}" target="_blank" title="Visit Link: {{ $link->url }}">{{ $link->description }}</a>
</li>
@endforeach
Include the following content in your index.blade.php
file. Feel free to customize the title and other information in the page as you wish:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sammy's Awesome Links</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title">
Check out my awesome links
</h1>
<p class="subtitle">
You can include a little description here.
</p>
<ul>
@foreach ($links as $link)
<li>
<a href="{{ $link->url }}" target="_blank" title="Visit Link: {{ $link->url }}">{{ $link->description }}</a>
</li>
@endforeach
</ul>
</div>
</section>
</body>
</html>
Save the file when you’re done.
Now go to your browser to check the results. You should be able to access your application at port 8000
of either localhost
or your remote server’s IP address, in case you are using a remote server as a development platform.
http://localhost:8000
You’ll see a page like this, showing all links present in your database from latest to first:
Your application is now fully-functional, but you can still improve the appearance of this starter page to make it more appealing to your audience.
Now that the base template is ready, you can include a few optional CSS customizations to style the page using some of the features available in Bulma, in addition to custom styles.
To give this page a new look, you can start by setting up a full page background. In this guide, we’ll use a DigitalOcean Wallpaper, but as an alternative you can also use a personal image or an image from a free stock photo website such as unsplash. You’ll need to obtain the image URL and use it to set up the background
CSS property for the html
element. A few other properties can be adjusted to make sure the image is centralized.
html {
background: url("https://i.imgur.com/BWIdYTM.jpeg") no-repeat center center fixed;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
To style the list of links, you might want to replace the <li>
elements for each link with box components, and include the link URL as a paragraph under the link description.
@foreach ($links as $link)
<div class="box link">
<h3><a href="{{ $link->url }}" target="_blank" title="Visit Link: {{ $link->url }}">{{ $link->description }}</a></h3>
<p>{{$link->url}}</p>
</div>
@endforeach
Finally, you can create a couple additional CSS styles to customize the appearance of the link text.
div.link h3 {
font-size: large;
}
div.link p {
font-size: small;
color: #718096;
}
The following Blade template contains all suggested implementations. Replace your current index.blade.php
file contents with:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sammy's Awesome Links</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
<style>
html {
background: url("https://i.imgur.com/BWIdYTM.jpeg") no-repeat center center fixed;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
div.link h3 {
font-size: large;
}
div.link p {
font-size: small;
color: #718096;
}
</style>
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title">
Check out my awesome links
</h1>
<p class="subtitle">
You can include a little description here.
</p>
<section class="links">
@foreach ($links as $link)
<div class="box link">
<h3><a href="{{ $link->url }}" target="_blank" title="Visit Link: {{ $link->url }}">{{ $link->description }}</a></h3>
<p>{{$link->url}}</p>
</div>
@endforeach
</section>
</div>
</section>
</body>
</html>
Save the file when you’re done.
Now reload your browser and you’ll see the updated page:
Congratulations, you have built a fully functional Laravel application. You have seen how to bootstrap a containerized PHP development environment and how to create a Laravel application from scratch, how to create database migrations and Eloquent models, how to create Artisan commands, and how to style a custom blade template.
The goal of this series was to provide a project-oriented path to get started in Laravel, using a links landing page as example application. You are strongly encouraged to review and experiment with each individual topic to get more familiar with the framework capabilities.
If you’d like to make this application accessible to the general public, you have several options for deploying it to a remote server. Our guide on How To Install and Configure Laravel with Nginx on Ubuntu 20.04 goes over how to deploy a Laravel application to a remote web server, but you might also check out our guide on How to Deploy a Laravel application to Kubernetes with Helm if you would like to use Kubernetes and a containerized environment to serve your application to the public.
For more information about the subjects we discussed in this series, check the following resources:
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.