In this article I want to give you a guide on how to setup your development environment on macOS and start developing PHP applications with Laravel.
Contents
- Setup Valet
- Create a Project
- Laravel Project Structure
- Blade Templates
- Database Handling
- Shared elements
Setup Valet
Valet is a light-weight Laravel development environment for macOS, that comes with Nginx. It provides proxies to point all requests on a configured domain to point to installed sites on your local machine.
Install components with Homebrew
Go to Homebrew and copy the installation command and run it in your terminal.
# Update homebrew
brew update
# Install PHP
brew install php
# Install composer
brew install composer
Run the following command and see if it says "composer" anywhere:
echo $PATH
If not, you have to add composer to your path. Add the following line to your shell configuration. I use zsh as my shell, so I can add it to ~/.zshrc
export PATH=$PATH:$HOME/.composer/vendor/bin
Refresh your shells configuration:
# Refresh zsh configuration
source ~/.zshrc
Install Valet
# Actually install valet with composer
composer global require laravel/valet
valet install
composer global require laravel/installer
Create a Project
Make sure to navigate to the project folder, where you want to create the Laravel project.
# Create new project
laravel new PROJECT_NAME
Use Valet
Tell Valet to use the current folder as a Laravel project.
valet park
You can now open your project in your IDE of choice and browse to your projects URL (e.g. http://PROJECT_FOLDER.test)
If you want to see all "parked" projects execute the following.
valet parked
Laravel Project Structure
Laravel is organized in the Model-View-Controller structure.
MVC
Models are found under /app/Models. These are the data models that might be generated with the help of the Laravel CLI tools.
Views are found under /resources/views. The views are usually .blade.php templates, but they don't have to.
Controllers are found under /app/Http/Controllers. The controllers are used to handle the business logic and to get the right data with the help of the models, to pass that down to the requested view.
Routes
Laravel can configure routes for "normal" web applications as well as for an API.
"Normal" routes are configured under /routes/web.php and API routes are configured under /routes/api.php.
Route configuration
Routes can be configured with absolute paths or with dynamic parameters (wildcards) as follows.
// Get all
Route::get('/', function() {
return view('listings', [
'heading' => 'Latest Listings',
'listings' => Listing::all()
]);
});
// Get by id
Route::get('/listings/{id}', function($id) {
return view('listing', [
'listing' => Listing::find($id)
]);
});
Query string parameters might be read with the help of the Illuminate\Http\Request
class.
use Illuminate\Http\Request;
// Get query string parameters
Route::get('/search', function(Request $request) {
return view('search', [
'name' => $request->name,
'city' => $request->city
]);
});
It is also possible to add constraints to the route, to only allow specific values.
// Get by id -> id has to be a number
Route::get('/posts/{id}', function($id) {
return view('post', [
'post' => Post::find($id)
]);
})->where('id', '[0-9]+');
In order to return a json from an API route you might do the following.
Route::get('/posts', function() {
return response()->json([
'posts' => [
[
'title' => 'Post One'
]
]
]);
});
Calling a controller action from a route
If you want to call a controller action in a route you first need to create a controller.
php artisan make:controller ListingController
In the controller you can create public functions for all your actions.
// Show all listings
public function index() {
return view('listings', [
'listings' => Listing::all()
]);
}
// Show single listing
public function show(Listing $listing) {
return view('listing', [
'listing' => $listing
]);
}
The route definition can than be changed as follows, where we pass in the controller class and the action/function name.
// Get all
Route::get('/', [ListingController::class, 'index']);
// Get by id
Route::get('/listings/{listing}', [ListingController::class, 'show']);
Route action naming conventions
Common Resource Routes in Laravel are as follows.
index - Show all items
show - Show a single item
create - Show a form to create a new item
store - Store a new item
edit - Show a form to edit an existing item
update - Update an existing item
destroy - Delete an existing item
Configuration
The different system configuration files can be found under /config.
Database
In the /config/database.php file you can configure what type of database should be used (e.g. mysql or sqlite, etc.).
To configure the database connection you can edit the .env file in the projects root folder.
Blade Templates
Blade is a simple templating engine that is already included in Laravel. Blade templates are compiled into plain PHP code.
Passing data to views
Blade views (templates) may be returned from routes or controllers using the global view helper.
Route::get('/', function() {
return view('greeting', ['name' => 'Finn']);
});
Displaying data in views
The name variable can be displayed in the blade template as follows.
Hello, {{ $name }}
Blade directives
Blade directives are escaped by the @ symbol. These directives function identically to their PHP counterparts. A detailed documentation may be found under https://laravel.com/docs/9.x/blade
@if (count($records) === 1)
<p>I have one record!</p>
@elseif (count($records) > 1)
<p>I have multiple records!</p>
@else
<p>I dont have any records!</p>
@endif
<h1>{{ $heading }}</h1>
@if(count($listings) == 0)
<p>No listings found</p>
@endif
@foreach($listings as $listing)
<h2>
<a href="/listings/{{$listing['id']}}">{{$listing['title']}}</a>
</h2>
<p>{{$listing['description']}}</p>
@endforeach
An alternative to check for the existance of items is the @unless
directive.
@unless(count($listings) == 0)
@foreach($listings as $listing)
<h2>{{$listing['title']}}</h2>
<p>
{{$listing['description']}}
</p>
@endforeach
@else
<p>No listings found</p>
@endunless
Database Handling
Laravel support different database systems like MySQL, SQLite and others.
MySQL
# Start MySQL server
mysql.server start
# Login with root
mysql -u root -p
# Stop MySQL server
mysql.server stop
1. You should create a dedicated MySQL user for your project
CREATE USER 'someuser'@'localhost' IDENTIFIED BY 'somepassword';
2. Create a database
CREATE DATABASE somedatabasename;
3. Grant the newly created user access to the database
GRANT ALL PRIVILEGES ON somedatabasename.* TO 'someuser'@'localhost';
FLUSH PRIVILEGES;
4. Update the .env file in the project root with the database settings
Migrations
The database schema / structure is managed with database migrations. The migrations can be found under /database/migrations.
Create a new migration
To create a new migration to create a table named listings execute the following command.
php artisan make:migration create_listings_table
Add table columns in migration
Open the newly created migration file (something like 2023_02_17_101605_create_listings_table.php) and add the missing columns in the up method.
public function up()
{
Schema::create('listings', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('tags');
$table->string('company');
$table->string('location');
$table->string('email');
$table->string('website');
$table->longText('description');
$table->timestamps();
});
}
Run migrations
In order to apply the migrations to the database, execute the following command.
php artisan migrate
Seed database
To generate "dummy" data in your database you can update the run method found in the /database/seeders/DatabaseSeeder.php file. Here you can call a factory method.
To actually execute the database seed, execute the following command.
php artisan db:seed
Refresh / Wipe database
To refresh / wipe the database you can execute the following command. This will re-run the migrations and re-create all the tables. Attention: This will delete the table data
php artisan migrate:refresh
If you want to refresh the database, but also want to execute the database seeding you can do the following.
php artisan migrate:refresh --seed
Creating models
Laravel uses Eloquent as a default ORM. To create an Eloquent model you can execute the following command.
php artisan make:model ModelName
Creating factories
Factories are stored under /database/factories and can be created as follows.
php artisan make:factory FactoryName
In the definition function you can create the objects with the help of faker.
public function definition()
{
return [
'title' => $this->faker->sentence(),
'tags' => 'laravel, api, backend',
'company' => $this->faker->company(),
'email' => $this->faker->companyEmail(),
'website' => $this->faker->url(),
'location' => $this->faker->city(),
'description' => $this->faker->paragraph(5),
];
}
Shared elements
It is possible to extend a view in Laravel. By doing so it is possible to define a general page layout in one file and extend this layout by side specific content.
You may create a _layout.blade.php file under /resources/views/. Add the following to it, where you want to include the side specific content.
@yield('content')
When creating the actual side layout you can extend the general page layout as follows.
@extends('_layout')
@section('content')
<h2>{{$listing['title']}}</h2>
<p>{{$listing['description']}}</p>
@endsection
Partials
Sometimes you want to re-use a UI element on multiple pages. For this you can include a partial into your side.
Create the folder /resources/views/partials and add for example a file called _search.blade.php and add your layout (etc.) to it. Then you can include it in the different pages, where you want it to appear as follows.
@include('partials/_search')
Components
Components are similar to partials with the difference that you are able to provide data (e.g. a Model) to a component.
Create the folder /resources/views/components and add for example a file called listing-card.blade.php and add your layout (etc.) to it. In order to receive data that was passed to the component add the following.
@props(['listing'])
<h3>
<a href="/listings/{{$listing->id}}">{{$listing->title}}</a>
</h3>
In order to use a component and pass data to it call it as follows.
@foreach($listings as $listing)
<x-listing-card :listing="$listing"/>
@enddoreach
Enclosing component
If you want to use a component to enclose something else like if you would create a card component with a specific background and border, you can do the following.
Add a new component and insert a slot for the positioning of the content to be enclosed. In order to define default css classes you can use the $attributes->merge
function.
<div {{$attributes->merge(['class' => 'bg-gray-50 border border-gray-200 rounded p-6'])}}>
{{$slot}}
</div>
And use it like a "normal" HTML tag. The provided css class will be added to the default classes that are defined in the component itself.
<x-card class="p-10">
<p>Something in the card component</p>
</x-card>
Top comments (1)
Have you tried ServBay.dev?
It's a much easier tool for PHP developers, providing a user-friendly experience, especially for beginners. It supports all versions of PHP, MariaDB, PostgreSQL, as well as Redis and Memcached. You can run multiple PHP instances simultaneously and switch between them effortlessly. It also offers easy updates without the need to configure environment variables. This tool has greatly simplified my PHP development and is definitely worth trying!