Laravel: Difference between revisions
(54 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
== Requirements == | == Requirements == | ||
* PHP 7 | * PHP 7.3 (Laravel 8.0) | ||
* OpenSSL extension | * OpenSSL extension | ||
* PDO extension | * PDO extension | ||
Line 9: | Line 9: | ||
* Ctype extension | * Ctype extension | ||
* JSON extension | * JSON extension | ||
* BCMath extension | |||
* Fileinfo extension | |||
Most modern servers and distros will have all of the above available as packages, and most of the extensions are useful beyond just Laravel. | |||
== Projects == | == Projects == | ||
Line 14: | Line 18: | ||
Create a new project by running: | Create a new project by running: | ||
laravel new blog | |||
Add everything to Git with: | Add everything to Git with: | ||
Line 31: | Line 35: | ||
Run <code>php artisan config:cache</code> as part of production deployment. | Run <code>php artisan config:cache</code> as part of production deployment. | ||
<code>dd($var)</code> dumps <code>$var</code> and then calls <code>die()</code>. <code>dd(request->all());</code> will print all the request variables. | <code>dd($var)</code> dumps <code>$var</code> and then calls <code>die()</code>. <code>dd(request->all());</code> will print all the request variables. | ||
Line 50: | Line 52: | ||
}); | }); | ||
}); | }); | ||
=== Maintenance mode === | |||
<code>php artisan down</code> will put a site into maintenance mode (returns a 503 error to anyone who visits). | |||
Laravel 8.x has extra functionality for maintenance mode, including a secret key that allows access during maintenance mode and a render option which pre-renders the views (so that if <code>composer install</code> fails users won't see an error). | |||
== Configuration == | == Configuration == | ||
Most configuration is stored in <code>.env</code>, which will be auto-generated by <code>composer create-project</code>. | Most per-instance configuration is stored in <code>.env</code>, which will be auto-generated by <code>composer create-project</code>. | ||
Global configuration options are set in <code>config/app.php</code>. | |||
=== Changing Faker locale === | |||
To change the Faker locale for the application as a whole, edit the following line in <code>config/app.php</code>: | |||
'faker_locale' => 'en_GB' | |||
This will ensure that Faker creates UK data, e.g. for addresses and postcodes. | |||
== Routing == | == Routing == | ||
Line 169: | Line 187: | ||
request('field_name') | request('field_name') | ||
Mass assignment of request data (e.g. <code>request->all()</code>) is dangerous because the user can send anything they want in the request data, and therefore could alter fields such as <code>id</code>. You can protect against this using <code>protected $fillable = []</code> (allow) or <code>protected $guarded = []</code> (deny) in the model definition. | |||
== Views == | == Views == | ||
Line 198: | Line 218: | ||
<title>@yield('title', 'Default title')</title> | <title>@yield('title', 'Default title')</title> | ||
== Authentication == | It it also possible to take action depending on whether a section is defined: | ||
@hasSection('heading') | |||
<nowiki><h1>@yield('heading')</h1></nowiki> | |||
@endif | |||
Partials can be included with: | |||
@include('errors') | |||
The above will include the contents of <code>errors.blade.php</code>. | |||
=== Dates === | |||
Laravel uses Carbon for date handling. If you want dates from a database (e.g. MySQL DATE columns) to be converted into Carbon instances automatically, add these fields to the <code>$dates</code> member of the model: | |||
<code>protected $dates = ['start_date', 'end_date']</code> | |||
This allows you to format the fields within Blade: | |||
<code>$model['start_date']->format('d/m/Y')</code> | |||
== Authentication and Authorisation == | |||
5.x: <code>php artisan make:auth</code> creates a skeleton login and authentication system. | |||
6.x: <code>composer require laravel/ui && php artisan ui vue --auth && npm install && npm run dev</code> | |||
<code>Auth::routes()</code> generates all the standard routes for authentication. | |||
The authentication system is middleware so you can wrap all the routes into a group if they require authentication. The middleware can be used in a controller or the routes file. | |||
For routes which should '''not''' be accessible to logged in users (or, to put it another way, should only be accessible to guests), the <code>guest</code> / <code>RedirectIfAuthenticated</code> middleware can be used: | |||
Route::get('/register', 'UserController@register')->name('register')->middleware('guest'); | |||
Laravel has an <code>abort_if</code> helper for 403 (or any other status code) responses: | |||
abort_if($project->owner_id !== auth()->id(), 403); | |||
<code>abort_unless</code> does the same, but with the logic inverted: | |||
abort_unless($project->owner_id === auth()->id(), 403); | |||
=== Policies === | |||
Policies are a cleaner way than using <code>abort</code>. A policy can be created using the <code>make:policy</code> command: | |||
php artisan make:policy ProjectPolicy | |||
Policies are saved in <code>app/Policies/</code>. | |||
Adding the <code>model</code> parameter creates a policy with boilerplate for the given model: | |||
php artisan make:policy ProjectPolicy --model=Project | |||
Policies generated using the ModelNamePolicy convention (e.g. Project -> ProjectPolicy) will be discovered automatically by Laravel. Other policies must be registered in <code>app/Providers/AuthServiceProvider.php</code> | |||
protected $policies = [ | |||
'App\Project' => 'App\Policies\ProjectPolicy' | |||
]; | |||
Finally, inside the controller method: | |||
$this->authorize('view', $project); | |||
The above will call the <code>view</code> method of <code>App\Policies\ProjectPolicy</code>. | |||
== Eloquent == | == Eloquent == | ||
Line 236: | Line 322: | ||
<code>php artisan migrate --seed</code> will run a seeder along with the migration. | <code>php artisan migrate --seed</code> will run a seeder along with the migration. | ||
==== Dates ==== | |||
When saving dates to the database, you will often need to convert from a locale-specific format (e.g. dd/mm/yyyy for GB dates) to YYYY-MM-DDDD. This can be accomplished by using Carbon after you have validated the date in the locale-specific format: | |||
<code>$attributes['start_date'] = Carbon::parse(Carbon::createFromFormat(config('app.date_formats.en-gb.php'), $attributes['start_date']))->toDateString();</code> | |||
==== Seeding ==== | ==== Seeding ==== | ||
Line 242: | Line 334: | ||
php artisan make:seeder UsersTableSeeder | php artisan make:seeder UsersTableSeeder | ||
==== Dumping database queries ==== | |||
Database queries can be dumped using the <code>DB::listen</code> function: | |||
DB::listen(function ($query) { | |||
dump($query->sql); | |||
}); | |||
== Forms == | == Forms == | ||
Line 253: | Line 353: | ||
To submit a form with any method other than GET or POST, use POST and then add the following to the form: | To submit a form with any method other than GET or POST, use POST and then add the following to the form: | ||
<nowiki> | <nowiki>@method('DELETE')</nowiki> | ||
=== Validation === | |||
Laravel will always supply an <code>$errors</code> object, even if there are no errors. | |||
Previous form values can be retrieved using <code>old('input-name')</code>. | |||
Basic validation example: | |||
$validated = request()->validate( | |||
'title' => ['required, 'min:5', 'max:255'] | |||
); | |||
Something::create($validated); | |||
Example: [https://gist.github.com/JeffreyWay/bb70191eb4ed84e51d9d310b0b56c14b Laravel From Scratch: Episode 15 - Two Layers of Validation] | |||
== Assets == | |||
CSS and JavaScript assets are built using Laravel Mix. | |||
<code>npm install</code> - Install Mix. | |||
<code>npm run dev</code> - Compile assets in development (no minification, debugging enabled). | |||
<code>npm run production</code> - Compile assets in production (minification, no debugging). | |||
=== Versioning === | |||
In <code>webpack.mix.js</code>, add <code>.version()</code> to <code>mix.js</code>: | |||
mix.js('resources/js/app.js', 'public/js') | |||
.sass('resources/sass/app.scss', 'public/css') | |||
.version(); | |||
Then change any references to <code>asset('js/app.js')</code> to <code>mix('js/app.js')</code> and <code>asset('css/app.css')</code> to <code>mix('css/app.css')</code>. This will produce URLs containing a hash of the file, which will force browsers to download a new version when there are changes. | |||
=== Additional libraries === | |||
Example: jQuery UI | |||
<code>npm install jquery-ui</code> - Installs jQuery UI as a dependency. | |||
Add to <code>resources/js/app.js</code>: | |||
<code>import 'jquery-ui/ui/widgets/datepicker.js';</code> | |||
Also add the following line for en-GB localisation: | |||
<code>import 'jquery-ui/ui/i18n/datepicker-en-GB.js';</code> | |||
Add to <code>resources/sass/app.scss</code>: | |||
<code>@import '~jquery-ui/themes/base/all.css';</code> | |||
== Artisan == | == Artisan == | ||
Line 259: | Line 413: | ||
<code>php artisan serve</code> starts the PHP built-in server. | <code>php artisan serve</code> starts the PHP built-in server. | ||
<code>php artisan make:model Club - | <code>php artisan make:model Club -a</code> creates a model with a migration, factory and resource controller. This is generally the command to use to start a new model. | ||
<code>php artisan make: | <code>php artisan make:model Club -m -c -r</code> creates a model with a migration and a resource controller. | ||
<code>php artisan make:controller ClubsController -m Club --resource</code> creates a controller with resource routes for an existing model <code>Club</code> | <code>php artisan make:controller ClubsController -m Club --resource</code> creates a controller with resource routes for an existing model <code>Club</code> | ||
Line 282: | Line 436: | ||
* [https://laracasts.com/ Laracasts] | * [https://laracasts.com/ Laracasts] | ||
* [https://github.com/barryvdh/laravel-debugbar Laravel Debugbar] - Debugging bar showing queries executed etc. | |||
* [https://github.com/laravel/framework/issues/20719 #20719] - Bug report for <code>artisan key:generate</code>. Closed as effectively WONTFIX. Seems to no longer be an issue in most cases as the installer now generates a key and adds it to <code>.env</code>. | * [https://github.com/laravel/framework/issues/20719 #20719] - Bug report for <code>artisan key:generate</code>. Closed as effectively WONTFIX. Seems to no longer be an issue in most cases as the installer now generates a key and adds it to <code>.env</code>. | ||
* [https://laravel-news.com/larastan larastan] - Static checking for Laravel code (based on top of PHPStan). | * [https://laravel-news.com/larastan larastan] - Static checking for Laravel code (based on top of PHPStan). | ||
Line 287: | Line 442: | ||
* [https://medium.com/@shakyShane/laravel-docker-part-1-setup-for-development-e3daaefaf3c Laravel + Docker Part 1 - setup for Development] | * [https://medium.com/@shakyShane/laravel-docker-part-1-setup-for-development-e3daaefaf3c Laravel + Docker Part 1 - setup for Development] | ||
* [https://medium.com/@shakyShane/laravel-docker-part-2-preparing-for-production-9c6a024e9797 Laravel + Docker Part 2 - preparing for production] | * [https://medium.com/@shakyShane/laravel-docker-part-2-preparing-for-production-9c6a024e9797 Laravel + Docker Part 2 - preparing for production] | ||
* [https://laravel-news.com/laravel-money Laravel Money] | |||
* [https://simonkollross.de/posts/creating-encrypted-backups-of-laravel-apps Creating encrypted backups of Laravel apps] | |||
* [https://jasonmccreary.me/articles/start-testing-laravel/ Start testing your Laravel applications] | |||
* [https://laravel.com/docs/billing Laravel Cashier] - Integrations to Stripe's subscription billing service. | |||
* [https://freek.dev/1720-how-to-clean-up-huge-tables-in-laravel-with-ease How to clean up huge tables in Laravel with ease] - Blog post and package for cleanup of records in large tables. | |||
* [https://tighten.co/blog/converting-a-legacy-app-to-laravel/ Legacy to Laravel: How to Modernize an Aging PHP Application] | |||
[[Category:PHP]] | [[Category:PHP]] |
Latest revision as of 15:09, 13 October 2020
Requirements
- PHP 7.3 (Laravel 8.0)
- OpenSSL extension
- PDO extension
- MBstring extension
- Tokenizer extension
- XML extension
- Ctype extension
- JSON extension
- BCMath extension
- Fileinfo extension
Most modern servers and distros will have all of the above available as packages, and most of the extensions are useful beyond just Laravel.
Projects
Create a new project by running:
laravel new blog
Add everything to Git with:
git init git add . git commit
General
./vendor/bin/phpunit
runs all unit tests (tests/*Test.php
).
Database credentials and other configuration goes in .env
.
All variables set in .env
will be loaded into the $_ENV
superglobal and are also available via the env
helper: env('APP_DEBUG', false)
. The second parameter is the default, which will be returned if no environment variable exists for the given key.
Run php artisan config:cache
as part of production deployment.
dd($var)
dumps $var
and then calls die()
. dd(request->all());
will print all the request variables.
Use the route()
helper instead of url()
to refer to routes.
Route groups allow you to group routes together and share configuration settings without having to repeat them for each route.
Parameterised subdomain routing can be used for multitenancy sites where each client gets its own subdomain:
Route::group(['domain' => '{client}.example.org'], function() { Route::get('/', function($client) { }); Route::get('/users/{$id}', function($client, $id) { }); });
Maintenance mode
php artisan down
will put a site into maintenance mode (returns a 503 error to anyone who visits).
Laravel 8.x has extra functionality for maintenance mode, including a secret key that allows access during maintenance mode and a render option which pre-renders the views (so that if composer install
fails users won't see an error).
Configuration
Most per-instance configuration is stored in .env
, which will be auto-generated by composer create-project
.
Global configuration options are set in config/app.php
.
Changing Faker locale
To change the Faker locale for the application as a whole, edit the following line in config/app.php
:
'faker_locale' => 'en_GB'
This will ensure that Faker creates UK data, e.g. for addresses and postcodes.
Routing
Routes are defined in routes/web.php
and routes/api.php
.
Simple route definitions can be implemented by using closures, e.g.
Route::get('/about', function() { return 'About'; });
However, this can become unwieldy for large sites. Applications using closures also cannot take advantage of Laravel's route caching.
A common alternative to closures is to pass the name and method of a controller as a string:
Route::get('/about', 'WelcomeController@about');
This would call the about
method of the App\Http\Controllers\WelcomeController
controller.
Regular expressions can be set for parameters:
Route::get('users/{id}', function ($id) { })->where('id', '[0-9]+');
Routes are matched top to bottom, so more specific regular expressions should be defined first.
Routes can be grouped, which allows common configuration settings to be defined once.
All defined routes can be listed by running:
php artisan route:list
Routes can be cached by running:
php artisan route:cache
However, from now on Laravel will only look at the cache, so you must run the above command each time you make any changes to the routing file.
Parametised subdomain routing is also available for multi-tenancy applications, where each tenant gets its own subdomain (client1.example.org, client2.example.org etc.)
Naming conventions
Convention is to call a route the plural of the resource name, followed by a period, followed by the action. For example: photos.create
. Named routes can be used with: route('photos.create')
or, if parameters are required: route('photos.edit(['id' => 1])')
.
A route name can be added like so:
Route::get('/', function () { return view('welcome'); })->name('welcome');
Route conventions:
- Index:
GET /photos
- Create:
GET /photos/create
- Store:
POST /photos
- Edit:
GET /photos/1/edit
- Update:
PATCH /photos/1
- Delete:
DELETE /photos/1
- Show:
GET /photos/1
Or:
Route::resource('photos', 'PhotosController');
Controllers
Create a controller by running:
php artisan make:controller PagesController
This will create a file:
app/Http/Controllers/PagesController.php
Add methods such as:
public function about() { return view('about'); }
Route them (routes/web.php
):
Route::get('/about', 'PagesController@about')
Create a resource controller with:
php artisan make:controller TasksController --resource
This will add methods such as index
, create
etc.
You can build routes for all these resources like so:
Route::resource('tasks', 'TasksController');
php artisan route:list
will list all of your routes.
Request data
There are two ways to access request data within a controller:
Input::get('field_name')
Or you can inject a Request:
public function create(\Illuminate\Http\Request $request) { $request->input('field_name') }
Laravel: Up and Running (p.41) claims: 'this is actually how I and many other Laravel developers prefer to get the user input'.
In later versions of Laravel:
request('field_name')
Mass assignment of request data (e.g. request->all()
) is dangerous because the user can send anything they want in the request data, and therefore could alter fields such as id
. You can protect against this using protected $fillable = []
(allow) or protected $guarded = []
(deny) in the model definition.
Views
Views are stored under: resources/views
. return view('about');
will return resources/views/about.blade.php
.
Templates
By default Laravel supports PHP templates and its own templating system called Blade. For developers who prefer Twig, the TwigBridge component provides a bridge which allows files ending .twig
to be loaded automatically.
Blade
Defaults can be provided if a variable is not set: Template:$title or 'Default'
Layouts can be created as normal Blade files, with a .blade.php
extension. The @yield('yield_name')
keyword allows you to mark placeholders where data should be inserted from templates which use the layout.
Templates can use layouts via: @extends('layout')
. Placeholders can be filled with:
@section('yield_name') Some content here @endsection
Short placeholders can also be filled using a single @section
with the second parameter as the data to be inserted:
@section('yield_name', 'Some content')
By default a yield
which is not populated will be blank. However, you can provide a default via the second parameter, such as:
<title>@yield('title', 'Default title')</title>
It it also possible to take action depending on whether a section is defined:
@hasSection('heading') <h1>@yield('heading')</h1> @endif
Partials can be included with:
@include('errors')
The above will include the contents of errors.blade.php
.
Dates
Laravel uses Carbon for date handling. If you want dates from a database (e.g. MySQL DATE columns) to be converted into Carbon instances automatically, add these fields to the $dates
member of the model:
protected $dates = ['start_date', 'end_date']
This allows you to format the fields within Blade:
$model['start_date']->format('d/m/Y')
Authentication and Authorisation
5.x: php artisan make:auth
creates a skeleton login and authentication system.
6.x: composer require laravel/ui && php artisan ui vue --auth && npm install && npm run dev
Auth::routes()
generates all the standard routes for authentication.
The authentication system is middleware so you can wrap all the routes into a group if they require authentication. The middleware can be used in a controller or the routes file.
For routes which should not be accessible to logged in users (or, to put it another way, should only be accessible to guests), the guest
/ RedirectIfAuthenticated
middleware can be used:
Route::get('/register', 'UserController@register')->name('register')->middleware('guest');
Laravel has an abort_if
helper for 403 (or any other status code) responses:
abort_if($project->owner_id !== auth()->id(), 403);
abort_unless
does the same, but with the logic inverted:
abort_unless($project->owner_id === auth()->id(), 403);
Policies
Policies are a cleaner way than using abort
. A policy can be created using the make:policy
command:
php artisan make:policy ProjectPolicy
Policies are saved in app/Policies/
.
Adding the model
parameter creates a policy with boilerplate for the given model:
php artisan make:policy ProjectPolicy --model=Project
Policies generated using the ModelNamePolicy convention (e.g. Project -> ProjectPolicy) will be discovered automatically by Laravel. Other policies must be registered in app/Providers/AuthServiceProvider.php
protected $policies = [ 'App\Project' => 'App\Policies\ProjectPolicy' ];
Finally, inside the controller method:
$this->authorize('view', $project);
The above will call the view
method of App\Policies\ProjectPolicy
.
Eloquent
Eloquent is Laravel's Active Record implementation.
Database
Default text field sizes will break on MariaDB <= 10.2.1 and MySQL <= 5.7.6 due to the number of bytes used for each character. Upgrade to a supported version or add the following to app/Providers/AppServiceProviders.php
:
<?php use Illuminate\Support\Facades\Schema; public function boot() { Schema::defaultStringLength(191); }
The default character set used by Laravel from 5.4 onwards is utf8mb4
. Basically the maximum length of a key is 767 bytes, and if 4 bytes are used per character then this effectively limits key strings to 191 characters.
Returning a database query from a route casts it to JSON.
Migrations define the modifications which should be run when making the migration up and down. Migrations are run in order of date ascending, hence filenames like 2017_01_01_00000_create_tasks_table.php
.
Modifying or dropping a column requires doctrine/dbal
.
When using SQLite, you can only drop or modify one column per migration closure.
php artisan migrate
runs all outstanding migrations. Laravel keeps track of which migrations have already been run.
php artisan migrate:reset
rolls back all database migrations.
php artisan migrate:fresh
drops all tables and then runs all migrations.
php artisan migrate:status
shows the status of each migration, i.e. whether it has been run or not.
php artisan migrate --seed
will run a seeder along with the migration.
Dates
When saving dates to the database, you will often need to convert from a locale-specific format (e.g. dd/mm/yyyy for GB dates) to YYYY-MM-DDDD. This can be accomplished by using Carbon after you have validated the date in the locale-specific format:
$attributes['start_date'] = Carbon::parse(Carbon::createFromFormat(config('app.date_formats.en-gb.php'), $attributes['start_date']))->toDateString();
Seeding
Creating a seeder:
php artisan make:seeder UsersTableSeeder
Dumping database queries
Database queries can be dumped using the DB::listen
function:
DB::listen(function ($query) { dump($query->sql); });
Forms
All forms get CSRF protection by default, but you must include the following somewhere between <form>
and </form>
@csrf
This expands to a hidden form field called _token
. Failing to include this field will result in a 419 response.
To submit a form with any method other than GET or POST, use POST and then add the following to the form:
@method('DELETE')
Validation
Laravel will always supply an $errors
object, even if there are no errors.
Previous form values can be retrieved using old('input-name')
.
Basic validation example:
$validated = request()->validate( 'title' => ['required, 'min:5', 'max:255'] ); Something::create($validated);
Example: Laravel From Scratch: Episode 15 - Two Layers of Validation
Assets
CSS and JavaScript assets are built using Laravel Mix.
npm install
- Install Mix.
npm run dev
- Compile assets in development (no minification, debugging enabled).
npm run production
- Compile assets in production (minification, no debugging).
Versioning
In webpack.mix.js
, add .version()
to mix.js
:
mix.js('resources/js/app.js', 'public/js') .sass('resources/sass/app.scss', 'public/css') .version();
Then change any references to asset('js/app.js')
to mix('js/app.js')
and asset('css/app.css')
to mix('css/app.css')
. This will produce URLs containing a hash of the file, which will force browsers to download a new version when there are changes.
Additional libraries
Example: jQuery UI
npm install jquery-ui
- Installs jQuery UI as a dependency.
Add to resources/js/app.js
:
import 'jquery-ui/ui/widgets/datepicker.js';
Also add the following line for en-GB localisation:
import 'jquery-ui/ui/i18n/datepicker-en-GB.js';
Add to resources/sass/app.scss
:
@import '~jquery-ui/themes/base/all.css';
Artisan
php artisan serve
starts the PHP built-in server.
php artisan make:model Club -a
creates a model with a migration, factory and resource controller. This is generally the command to use to start a new model.
php artisan make:model Club -m -c -r
creates a model with a migration and a resource controller.
php artisan make:controller ClubsController -m Club --resource
creates a controller with resource routes for an existing model Club
tinker
php artisan tinker
is a command line tool which allows you to run queries.
File locations
Models: app/Club.php
Controllers: app/Http/Controllers/ClubsController.php
Version changes
- 5.6:
php artisan optimize
has been removed - no longer required.
Links
- Laracasts
- Laravel Debugbar - Debugging bar showing queries executed etc.
- #20719 - Bug report for
artisan key:generate
. Closed as effectively WONTFIX. Seems to no longer be an issue in most cases as the installer now generates a key and adds it to.env
. - larastan - Static checking for Laravel code (based on top of PHPStan).
- Simplify Project On-boarding with Laravel Homestead
- Laravel + Docker Part 1 - setup for Development
- Laravel + Docker Part 2 - preparing for production
- Laravel Money
- Creating encrypted backups of Laravel apps
- Start testing your Laravel applications
- Laravel Cashier - Integrations to Stripe's subscription billing service.
- How to clean up huge tables in Laravel with ease - Blog post and package for cleanup of records in large tables.
- Legacy to Laravel: How to Modernize an Aging PHP Application