Laravel

From Rixort Wiki
Jump to navigation Jump to search

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