diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e9560ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +_book/ +node_modules/ \ No newline at end of file diff --git a/application-testing.md b/application-testing.md deleted file mode 100644 index 57d06e5..0000000 --- a/application-testing.md +++ /dev/null @@ -1,351 +0,0 @@ -# Application Testing - -- [Introduction](#introduction) -- [Interacting With Your Application](#interacting-with-your-application) - - [Interacting With Links](#interacting-with-links) - - [Interacting With Forms](#interacting-with-forms) -- [Testing JSON APIs](#testing-json-apis) - - [Verifying Exact Match](#verifying-exact-match) - - [Verifying Structural Match](#verifying-structural-match) -- [Sessions / Authentication](#sessions-and-authentication) -- [Disabling Middleware](#disabling-middleware) -- [Custom HTTP Requests](#custom-http-requests) -- [PHPUnit Assertions](#phpunit-assertions) - - -## Introduction - -Laravel provides a very fluent API for making HTTP requests to your application, examining the output, and even filling out forms. For example, take a look at the test defined below: - - visit('/') - ->see('Laravel 5') - ->dontSee('Rails'); - } - } - -The `visit` method makes a `GET` request into the application. The `see` method asserts that we should see the given text in the response returned by the application. The `dontSee` method asserts that the given text is not returned in the application response. This is the most basic application test available in Laravel. - -You may also use the 'visitRoute' method to make a 'GET' request via a named route: - - $this->visitRoute('profile'); - - $this->visitRoute('profile', ['user' => 1]); - - -## Interacting With Your Application - -Of course, you can do much more than simply assert that text appears in a given response. Let's take a look at some examples of clicking links and filling out forms: - - -### Interacting With Links - -In this test, we will make a request to the application, "click" a link in the returned response, and then assert that we landed on a given URI. For example, let's assume there is a link in our response that has a text value of "About Us": - - About Us - -Now, let's write a test that clicks the link and asserts the user lands on the correct page: - - public function testBasicExample() - { - $this->visit('/') - ->click('About Us') - ->seePageIs('/about-us'); - } - -You may also check that the user has arrived at the correct named route using the `seeRouteIs` method: - - ->seeRouteIs('profile', ['user' => 1]); - - -### Interacting With Forms - -Laravel also provides several methods for testing forms. The `type`, `select`, `check`, `attach`, and `press` methods allow you to interact with all of your form's inputs. For example, let's imagine this form exists on the application's registration page: - -
- {{ csrf_field() }} - -
- Name: -
- -
- Accept Terms -
- -
- -
-
- -We can write a test to complete this form and inspect the result: - - public function testNewUserRegistration() - { - $this->visit('/register') - ->type('Taylor', 'name') - ->check('terms') - ->press('Register') - ->seePageIs('/dashboard'); - } - -Of course, if your form contains other inputs such as radio buttons or drop-down boxes, you may easily fill out those types of fields as well. Here is a list of each form manipulation method: - -Method | Description -------------- | ------------- -`$this->type($text, $elementName)` | "Type" text into a given field. -`$this->select($value, $elementName)` | "Select" a radio button or drop-down field. -`$this->check($elementName)` | "Check" a checkbox field. -`$this->uncheck($elementName)` | "Uncheck" a checkbox field. -`$this->attach($pathToFile, $elementName)` | "Attach" a file to the form. -`$this->press($buttonTextOrElementName)` | "Press" a button with the given text or name. - - -#### File Inputs - -If your form contains `file` inputs, you may attach files to the form using the `attach` method: - - public function testPhotoCanBeUploaded() - { - $this->visit('/upload') - ->attach($pathToFile, 'photo') - ->press('Upload') - ->see('Upload Successful!'); - } - - -### Testing JSON APIs - -Laravel also provides several helpers for testing JSON APIs and their responses. For example, the `json`, `get`, `post`, `put`, `patch`, and `delete` methods may be used to issue requests with various HTTP verbs. You may also easily pass data and headers to these methods. To get started, let's write a test to make a `POST` request to `/user` and assert that the expected data was returned: - - json('POST', '/user', ['name' => 'Sally']) - ->seeJson([ - 'created' => true, - ]); - } - } - -> {tip} The `seeJson` method converts the given array into JSON, and then verifies that the JSON fragment occurs **anywhere** within the entire JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. - - -### Verifying Exact Match - -If you would like to verify that the given array is an **exact** match for the JSON returned by the application, you should use the `seeJsonEquals` method: - - json('POST', '/user', ['name' => 'Sally']) - ->seeJsonEquals([ - 'created' => true, - ]); - } - } - - -### Verifying Structural Match - -It is also possible to verify that a JSON response adheres to a specific structure. In this scenario, you should use the `seeJsonStructure` method and pass it your expected JSON structure: - - get('/user/1') - ->seeJsonStructure([ - 'name', - 'pet' => [ - 'name', 'age' - ] - ]); - } - } - -The above example illustrates an expectation of receiving a `name` attribute and a nested `pet` object with its own `name` and `age` attributes. `seeJsonStructure` will not fail if additional keys are present in the response. For example, the test would still pass if the `pet` had a `weight` attribute. - -You may use the `*` to assert that the returned JSON structure has a list where each list item contains at least the attributes found in the set of values: - - get('/users') - ->seeJsonStructure([ - '*' => [ - 'id', 'name', 'email' - ] - ]); - } - } - -You may also nest the `*` notation. In this case, we will assert that each user in the JSON response contains a given set of attributes and that each pet on each user also contains a given set of attributes: - - $this->get('/users') - ->seeJsonStructure([ - '*' => [ - 'id', 'name', 'email', 'pets' => [ - '*' => [ - 'name', 'age' - ] - ] - ] - ]); - - -### Sessions / Authentication - -Laravel provides several helpers for working with the session during testing. First, you may set the session data to a given array using the `withSession` method. This is useful for loading the session with data before issuing a request to your application: - - withSession(['foo' => 'bar']) - ->visit('/'); - } - } - -Of course, one common use of the session is for maintaining state for the authenticated user. The `actingAs` helper method provides a simple way to authenticate a given user as the current user. For example, we may use a [model factory](#model-factories) to generate and authenticate a user: - - create(); - - $this->actingAs($user) - ->withSession(['foo' => 'bar']) - ->visit('/') - ->see('Hello, '.$user->name); - } - } - -You may also specify which guard should be used to authenticate the given user by passing the guard name as the second argument to the `actingAs` method: - - $this->actingAs($user, 'api') - - -### Disabling Middleware - -When testing your application, you may find it convenient to disable [middleware](/docs/{{version}}/middleware) for some of your tests. This will allow you to test your routes and controller in isolation from any middleware concerns. Laravel includes a simple `WithoutMiddleware` trait that you can use to automatically disable all middleware for the test class: - - withoutMiddleware(); - - $this->visit('/') - ->see('Laravel 5'); - } - } - - -### Custom HTTP Requests - -If you would like to make a custom HTTP request into your application and get the full `Illuminate\Http\Response` object, you may use the `call` method: - - public function testApplication() - { - $response = $this->call('GET', '/'); - - $this->assertEquals(200, $response->status()); - } - -If you are making `POST`, `PUT`, or `PATCH` requests you may pass an array of input data with the request. Of course, this data will be available in your routes and controller via the [Request instance](/docs/{{version}}/requests): - - $response = $this->call('POST', '/user', ['name' => 'Taylor']); - - -### PHPUnit Assertions - -Laravel provides a variety of custom assertion methods for [PHPUnit](https://phpunit.de/) tests: - -Method | Description -------------- | ------------- -`->assertResponseOk();` | Assert that the client response has an OK status code. -`->assertResponseStatus($code);` | Assert that the client response has a given code. -`->assertViewHas($key, $value = null);` | Assert that the response view has a given piece of bound data. -`->assertViewHasAll(array $bindings);` | Assert that the view has a given list of bound data. -`->assertViewMissing($key);` | Assert that the response view is missing a piece of bound data. -`->assertRedirectedTo($uri, $with = []);` | Assert whether the client was redirected to a given URI. -`->assertRedirectedToRoute($name, $parameters = [], $with = []);` | Assert whether the client was redirected to a given route. -`->assertRedirectedToAction($name, $parameters = [], $with = []);` | Assert whether the client was redirected to a given action. -`->assertSessionHas($key, $value = null);` | Assert that the session has a given value. -`->assertSessionHasAll(array $bindings);` | Assert that the session has a given list of values. -`->assertSessionHasErrors($bindings = [], $format = null);` | Assert that the session has errors bound. -`->assertHasOldInput();` | Assert that the session has old input. -`->assertSessionMissing($key);` | Assert that the session is missing a given key. diff --git a/artisan.md b/artisan.md index a910c6f..1afe87d 100644 --- a/artisan.md +++ b/artisan.md @@ -1,9 +1,10 @@ -# Console Commands +# Artisan Console - [Introduction](#introduction) - [Writing Commands](#writing-commands) - [Generating Commands](#generating-commands) - [Command Structure](#command-structure) + - [Closure Commands](#closure-commands) - [Defining Input Expectations](#defining-input-expectations) - [Arguments](#arguments) - [Options](#options) @@ -14,7 +15,7 @@ - [Prompting For Input](#prompting-for-input) - [Writing Output](#writing-output) - [Registering Commands](#registering-commands) -- [Programatically Executing Commands](#programatically-executing-commands) +- [Programmatically Executing Commands](#programmatically-executing-commands) - [Calling Commands From Other Commands](#calling-commands-from-other-commands) @@ -28,6 +29,12 @@ Every command also includes a "help" screen which displays and describes the com php artisan help migrate +#### Laravel REPL + +All Laravel applications include Tinker, a REPL powered by the [PsySH](https://github.com/bobthecow/psysh) package. Tinker allows you to interact with your entire Laravel application on the command line, including the Eloquent ORM, jobs, events, and more. To enter the Tinker environment, run the `tinker` Artisan command: + + php artisan tinker + ## Writing Commands @@ -36,10 +43,12 @@ In addition to the commands provided with Artisan, you may also build your own c ### Generating Commands -To create a new command, use the `make:command` Artisan command. This command will create a new command class in the `app/Console/Commands` directory. The generated command will include the default set of properties and methods that are present on all commands: +To create a new command, use the `make:command` Artisan command. This command will create a new command class in the `app/Console/Commands` directory. Don't worry if this directory does not exist in your application, since it will be created the first time you run the `make:command` Artisan command. The generated command will include the default set of properties and methods that are present on all commands: php artisan make:command SendEmails +Next, you will need to [register the command](#registering-commands) before it can be executed via the Artisan CLI. + ### Command Structure @@ -104,6 +113,48 @@ Let's take a look at an example command. Note that we are able to inject any dep } } + +### Closure Commands + +Closure based commands provide an alternative to defining console commands as classes. In the same way that route Closures are an alternative to controllers, think of command Closures as an alternative to command classes. Within the `commands` method of your `app/Console/Kernel.php` file, Laravel loads the `routes/console.php` file: + + /** + * Register the Closure based commands for the application. + * + * @return void + */ + protected function commands() + { + require base_path('routes/console.php'); + } + +Even though this file does not define HTTP routes, it defines console based entry points (routes) into your application. Within this file, you may define all of your Closure based routes using the `Artisan::command` method. The `command` method accepts two arguments: the [command signature](#defining-input-expectations) and a Closure which receives the commands arguments and options: + + Artisan::command('build {project}', function ($project) { + $this->info("Building {$project}!"); + }); + +The Closure is bound to the underlying command instance, so you have full access to all of the helper methods you would typically be able to access on a full command class. + +#### Type-Hinting Dependencies + +In addition to receiving your command's arguments and options, command Closures may also type-hint additional dependencies that you would like resolved out of the [service container](/docs/{{version}}/container): + + use App\User; + use App\DripEmailer; + + Artisan::command('email:send {user}', function (DripEmailer $drip, $user) { + $drip->send(User::find($user)); + }); + +#### Closure Command Descriptions + +When defining a Closure based command, you may use the `describe` method to add a description to the command. This description will be displayed when you run the `php artisan list` or `php artisan help` commands: + + Artisan::command('build {project}', function ($project) { + $this->info("Building {$project}!"); + })->describe('Build the project'); + ## Defining Input Expectations @@ -258,9 +309,9 @@ The `secret` method is similar to `ask`, but the user's input will not be visibl #### Asking For Confirmation -If you need to ask the user for a simple confirmation, you may use the `confirm` method. By default, this method will return `false`. However, if the user enters `y` in response to the prompt, the method will return `true`. +If you need to ask the user for a simple confirmation, you may use the `confirm` method. By default, this method will return `false`. However, if the user enters `y` or `yes` in response to the prompt, the method will return `true`. - if ($this->confirm('Do you wish to continue? [y|N]')) { + if ($this->confirm('Do you wish to continue?')) { // } @@ -325,7 +376,7 @@ For long running tasks, it could be helpful to show a progress indicator. Using $bar->finish(); -For more advanced options, check out the [Symfony Progress Bar component documentation](http://symfony.com/doc/2.7/components/console/helpers/progressbar.html). +For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/2.7/components/console/helpers/progressbar.html). ## Registering Commands @@ -336,8 +387,8 @@ Once your command is finished, you need to register it with Artisan. All command Commands\SendEmails::class ]; - -## Programatically Executing Commands + +## Programmatically Executing Commands Sometimes you may wish to execute an Artisan command outside of the CLI. For example, you may wish to fire an Artisan command from a route or controller. You may use the `call` method on the `Artisan` facade to accomplish this. The `call` method accepts the name of the command as the first argument, and an array of command parameters as the second argument. The exit code will be returned: diff --git a/authentication.md b/authentication.md index 669621b..99edacc 100644 --- a/authentication.md +++ b/authentication.md @@ -14,6 +14,7 @@ - [Other Authentication Methods](#other-authentication-methods) - [HTTP Basic Authentication](#http-basic-authentication) - [Stateless HTTP Basic Authentication](#stateless-http-basic-authentication) +- [Social Authentication](https://github.com/laravel/socialite) - [Adding Custom Guards](#adding-custom-guards) - [Adding Custom User Providers](#adding-custom-user-providers) - [The User Provider Contract](#the-user-provider-contract) @@ -23,7 +24,7 @@ ## Introduction -> {tip} **Want to get started fast?** Just run `php artisan make:auth` in a fresh Laravel application and navigate your browser to `http://your-app.dev/register` or any other URL that is assigned to your application. This single command will take care of scaffolding your entire authentication system! +> {tip} **Want to get started fast?** Just run `php artisan make:auth` and `php artisan migrate` in a fresh Laravel application. Then, navigate your browser to `http://your-app.dev/register` or any other URL that is assigned to your application. These two commands will take care of scaffolding your entire authentication system! Laravel makes implementing authentication very simple. In fact, almost everything is configured for you out of the box. The authentication configuration file is located at `config/auth.php`, which contains several well documented options for tweaking the behavior of the authentication services. @@ -74,7 +75,23 @@ When a user is successfully authenticated, they will be redirected to the `/home protected $redirectTo = '/'; -When a user is not successfully authenticated, they will be automatically redirected back to the login form. +If the redirect path needs custom generation logic you may define a `redirectTo` method instead of a `redirectTo` property: + + protected function redirectTo() + { + // + } + +> {tip} The `redirectTo` method will take precedence over the `redirectTo` attribute. + +#### Username Customization + +By default, Laravel uses the `email` field for authentication. If you would like to customize this, you may define a `username` method on your `LoginController`: + + public function username() + { + return 'username'; + } #### Guard Customization @@ -102,8 +119,12 @@ You may access the authenticated user via the `Auth` facade: use Illuminate\Support\Facades\Auth; + // Get the currently authenticated user... $user = Auth::user(); + // Get the currently authenticated user's ID... + $id = Auth::id(); + Alternatively, once a user is authenticated, you may access the authenticated user via an `Illuminate\Http\Request` instance. Remember, type-hinted classes will automatically be injected into your controller methods: middleware('auth'); @@ -181,7 +202,7 @@ We will access Laravel's authentication services via the `Auth` [facade](/docs/{ use Illuminate\Support\Facades\Auth; - class AuthController extends Controller + class LoginController extends Controller { /** * Handle an authentication attempt. @@ -273,7 +294,7 @@ To log a user into the application by their ID, you may use the `loginUsingId` m #### Authenticate A User Once -You may use the `once` method to log a user into the application for a single request. No sessions or cookies will be utilized, which means this method may be helpful when building a stateless API. The `once` method has the same signature as the `attempt` method: +You may use the `once` method to log a user into the application for a single request. No sessions or cookies will be utilized, which means this method may be helpful when building a stateless API: if (Auth::once($credentials)) { // @@ -282,9 +303,9 @@ You may use the `once` method to log a user into the application for a single re ## HTTP Basic Authentication -[HTTP Basic Authentication](http://en.wikipedia.org/wiki/Basic_access_authentication) provides a quick way to authenticate users of your application without setting up a dedicated "login" page. To get started, attach the `auth.basic` [middleware](/docs/{{version}}/middleware) to your route. The `auth.basic` middleware is included with the Laravel framework, so you do not need to define it: +[HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) provides a quick way to authenticate users of your application without setting up a dedicated "login" page. To get started, attach the `auth.basic` [middleware](/docs/{{version}}/middleware) to your route. The `auth.basic` middleware is included with the Laravel framework, so you do not need to define it: - Route::get('profile', function() { + Route::get('profile', function () { // Only authenticated users may enter... })->middleware('auth.basic'); @@ -326,7 +347,7 @@ You may also use HTTP Basic Authentication without setting a user identifier coo Next, [register the route middleware](/docs/{{version}}/middleware#registering-middleware) and attach it to a route: - Route::get('api/user', function() { + Route::get('api/user', function () { // Only authenticated users may enter... })->middleware('auth.basic.once'); @@ -341,7 +362,7 @@ You may define your own authentication guards using the `extend` method on the ` use App\Services\Auth\JwtGuard; use Illuminate\Support\Facades\Auth; - use Illuminate\Support\ServiceProvider; + use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { @@ -354,7 +375,7 @@ You may define your own authentication guards using the `extend` method on the ` { $this->registerPolicies(); - Auth::extend('jwt', function($app, $name, array $config) { + Auth::extend('jwt', function ($app, $name, array $config) { // Return an instance of Illuminate\Contracts\Auth\Guard... return new JwtGuard(Auth::createUserProvider($config['provider'])); @@ -395,7 +416,7 @@ If you are not using a traditional relational database to store your users, you { $this->registerPolicies(); - Auth::provider('riak', function($app, array $config) { + Auth::provider('riak', function ($app, array $config) { // Return an instance of Illuminate\Contracts\Auth\UserProvider... return new RiakUserProvider($app->make('riak.connection')); @@ -484,14 +505,26 @@ Laravel raises a variety of [events](/docs/{{version}}/events) during the authen * @var array */ protected $listen = [ + 'Illuminate\Auth\Events\Registered' => [ + 'App\Listeners\LogRegisteredUser', + ], + 'Illuminate\Auth\Events\Attempting' => [ 'App\Listeners\LogAuthenticationAttempt', ], + 'Illuminate\Auth\Events\Authenticated' => [ + 'App\Listeners\LogAuthenticated', + ], + 'Illuminate\Auth\Events\Login' => [ 'App\Listeners\LogSuccessfulLogin', ], + 'Illuminate\Auth\Events\Failed' => [ + 'App\Listeners\LogFailedLogin', + ], + 'Illuminate\Auth\Events\Logout' => [ 'App\Listeners\LogSuccessfulLogout', ], diff --git a/authorization.md b/authorization.md index ca0ee91..8974037 100644 --- a/authorization.md +++ b/authorization.md @@ -24,11 +24,12 @@ In addition to providing [authentication](/docs/{{version}}/authentication) serv Think of gates and policies like routes and controllers. Gates provide a simple, Closure based approach to authorization while policies, like controllers, group their logic around a particular model or resource. We'll explore gates first and then examine policies. -It is important to not view gates and policies as mutually exclusive for your application. Most applications will most likely contain a mixture of gates and policies, and that is perfectly fine! Gates are most applicable to actions which are not related to any model or resource, such as viewing an administrator dashboard. In contrast, policies should be used when you wish to authorize an action for a particular model or resource. +You do not need to choose between exclusively using gates or exclusively using policies when building an application. Most applications will most likely contain a mixture of gates and policies, and that is perfectly fine! Gates are most applicable to actions which are not related to any model or resource, such as viewing an administrator dashboard. In contrast, policies should be used when you wish to authorize an action for a particular model or resource. ## Gates + ### Writing Gates Gates are Closures that determine if a user is authorized to perform a given action and are typically defined in the `App\Providers\AuthServiceProvider` class using the `Gate` facade. Gates always receive a user instance as their first argument, and may optionally receive additional arguments such as a relevant Eloquent model: @@ -50,11 +51,15 @@ Gates are Closures that determine if a user is authorized to perform a given act ### Authorizing Actions -To authorize an action using gates, you should use the `allows` method. Note that you are not required to pass the currently authenticated user to the `allows` method. Laravel will automatically take care of passing the user into the gate Closure: +To authorize an action using gates, you should use the `allows` or `denies` methods. Note that you are not required to pass the currently authenticated user to these methods. Laravel will automatically take care of passing the user into the gate Closure: if (Gate::allows('update-post', $post)) { // The current user can update the post... - }); + } + + if (Gate::denies('update-post', $post)) { + // The current user can't update the post... + } If you would like to determine if a particular user is authorized to perform an action, you may use the `forUser` method on the `Gate` facade: @@ -62,6 +67,10 @@ If you would like to determine if a particular user is authorized to perform an // The user can update the post... } + if (Gate::forUser($user)->denies('update-post', $post)) { + // The user can't update the post... + } + ## Creating Policies @@ -172,8 +181,6 @@ When defining policy methods that will not receive a model instance, such as a ` // } -> {tip} If you used the `--model` option when generating your policy, all of the relevant "CRUD" policy methods will already be defined on the generated policy. - ### Policy Filters @@ -186,6 +193,8 @@ For certain users, you may wish to authorize all actions within a given policy. } } +If you would like to deny all authorizations for a user you should return `false` from the `before` method. If `null` is returned, the authorization will fall through to the policy method. + ## Authorizing Actions Using Policies @@ -281,17 +290,21 @@ As previously discussed, some actions like `create` may not require a model inst ### Via Blade Templates -When writing Blade templates, you may wish to display a portion of the page only if the user is authorized to perform a given action. For example, you may wish to show an update form for a blog post only if the user can actually update the post. In this situation, you may use the `@can` and `@cannot` directives. +When writing Blade templates, you may wish to display a portion of the page only if the user is authorized to perform a given action. For example, you may wish to show an update form for a blog post only if the user can actually update the post. In this situation, you may use the `@can` and `@cannot` family of directives: @can('update', $post) + @elsecan('create', $post) + @endcan @cannot('update', $post) + @elsecannot('create', $post) + @endcannot -These directives are convenient short-cuts for writing `@if` and `@unless` statements. The `@can` and `@cannot` statements above respectively translate to the following statements: +These directives are convenient shortcuts for writing `@if` and `@unless` statements. The `@can` and `@cannot` statements above respectively translate to the following statements: @if (Auth::user()->can('update', $post)) diff --git a/billing.md b/billing.md index 5388cd0..83f7e5d 100644 --- a/billing.md +++ b/billing.md @@ -30,7 +30,7 @@ ## Introduction -Laravel Cashier provides an expressive, fluent interface to [Stripe's](https://stripe.com) and [Braintree's](https://braintreepayments.com) subscription billing services. It handles almost all of the boilerplate subscription billing code you are dreading writing. In addition to basic subscription management, Cashier can handle coupons, swapping subscription, subscription "quantities", cancellation grace periods, and even generate invoice PDFs. +Laravel Cashier provides an expressive, fluent interface to [Stripe's](https://stripe.com) and [Braintree's](https://www.braintreepayments.com) subscription billing services. It handles almost all of the boilerplate subscription billing code you are dreading writing. In addition to basic subscription management, Cashier can handle coupons, swapping subscription, subscription "quantities", cancellation grace periods, and even generate invoice PDFs. > {note} If you're only performing "one-off" charges and do not offer subscriptions. You should not use Cashier. You should use the Stripe and Braintree SDKs directly. @@ -44,7 +44,7 @@ Laravel Cashier provides an expressive, fluent interface to [Stripe's](https://s First, add the Cashier package for Stripe to your `composer.json` file and run the `composer update` command: - "laravel/cashier": "~6.0" + "laravel/cashier": "~7.0" #### Service Provider @@ -112,7 +112,7 @@ For many operations, the Stripe and Braintree implementations of Cashier functio First, add the Cashier package for Braintree to your `composer.json` file and run the `composer update` command: - "laravel/cashier-braintree": "~1.0" + "laravel/cashier-braintree": "~2.0" #### Service Provider @@ -175,10 +175,10 @@ Next, You should configure the following options in your `services.php` file: Then you should add the following Braintree SDK calls to your `AppServiceProvider` service provider's `boot` method: - \Braintree_Configuration::environment(env('BRAINTREE_ENV')); - \Braintree_Configuration::merchantId(env('BRAINTREE_MERCHANT_ID')); - \Braintree_Configuration::publicKey(env('BRAINTREE_PUBLIC_KEY')); - \Braintree_Configuration::privateKey(env('BRAINTREE_PRIVATE_KEY')); + \Braintree_Configuration::environment(config('services.braintree.environment')); + \Braintree_Configuration::merchantId(config('services.braintree.merchant_id')); + \Braintree_Configuration::publicKey(config('services.braintree.public_key')); + \Braintree_Configuration::privateKey(config('services.braintree.private_key')); ### Currency Configuration @@ -199,7 +199,7 @@ To create a subscription, first retrieve an instance of your billable model, whi $user = User::find(1); - $user->newSubscription('main', 'monthly')->create($creditCardToken); + $user->newSubscription('main', 'monthly')->create($stripeToken); The first argument passed to the `newSubscription` method should be the name of the subscription. If your application only offers a single subscription, you might call this `main` or `primary`. The second argument is the specific Stripe / Braintree plan the user is subscribing to. This value should correspond to the plan's identifier in Stripe or Braintree. @@ -209,7 +209,7 @@ The `create` method will begin the subscription as well as update your database If you would like to specify additional customer details, you may do so by passing them as the second argument to the `create` method: - $user->newSubscription('main', 'monthly')->create($creditCardToken, [ + $user->newSubscription('main', 'monthly')->create($stripeToken, [ 'email' => $email, ]); @@ -221,7 +221,7 @@ If you would like to apply a coupon when creating the subscription, you may use $user->newSubscription('main', 'monthly') ->withCoupon('code') - ->create($creditCardToken); + ->create($stripeToken); ### Checking Subscription Status @@ -358,7 +358,7 @@ If the user cancels a subscription and then resumes that subscription before the The `updateCard` method may be used to update a customer's credit card information. This method accepts a Stripe token and will assign the new credit card as the default billing source: - $user->updateCard($creditCardToken); + $user->updateCard($stripeToken); ## Subscription Trials @@ -372,7 +372,7 @@ If you would like to offer trial periods to your customers while still collectin $user->newSubscription('main', 'monthly') ->trialDays(10) - ->create($creditCardToken); + ->create($stripeToken); This method will set the trial period ending date on the subscription record within the database, as well as instruct Stripe / Braintree to not begin billing the customer until after this date. @@ -398,6 +398,8 @@ If you would like to offer trial periods without collecting the user's payment m 'trial_ends_at' => Carbon::now()->addDays(10), ]); +> {note} Be sure to add a [date mutator](/docs/{{version}}/eloquent-mutators#date-mutators) for `trial_ends_at` to your model definition. + Cashier refers to this type of trial as a "generic trial", since it is not attached to any existing subscription. The `onTrial` method on the `User` instance will return `true` if the current date is not past the value of `trial_ends_at`: if ($user->onTrial()) { @@ -414,7 +416,7 @@ Once you are ready to create an actual subscription for the user, you may use th $user = User::find(1); - $user->newSubscription('main', 'monthly')->create($creditCardToken); + $user->newSubscription('main', 'monthly')->create($stripeToken); ## Handling Stripe Webhooks @@ -432,7 +434,7 @@ By default, this controller will automatically handle cancelling subscriptions t #### Webhooks & CSRF Protection -Since Stripe webhooks need to bypass Laravel's [CSRF protection](/docs/{{version}}/routing#csrf-protection), be sure to list the URI as an exception in your `VerifyCsrfToken` middleware or list the route outside of the `web` middleware group: +Since Stripe webhooks need to bypass Laravel's [CSRF protection](/docs/{{version}}/csrf), be sure to list the URI as an exception in your `VerifyCsrfToken` middleware or list the route outside of the `web` middleware group: protected $except = [ 'stripe/*', @@ -466,7 +468,7 @@ Cashier automatically handles subscription cancellation on failed charges, but i ### Failed Subscriptions -What if a customer's credit card expires? No worries - the Cashier webhook controller that can easily cancel the customer's subscription for you. As noted above, all you need to do is point a route to the controller: +What if a customer's credit card expires? No worries - Cashier includes a Webhook controller that can easily cancel the customer's subscription for you. As noted above, all you need to do is point a route to the controller: Route::post( 'stripe/webhook', @@ -481,7 +483,7 @@ That's it! Failed payments will be captured and handled by the controller. The c Both Stripe and Braintree can notify your application of a variety of events via webhooks. To handle Braintree webhooks, define a route that points to Cashier's webhook controller. This controller will handle all incoming webhook requests and dispatch them to the proper controller method: Route::post( - 'stripe/webhook', + 'braintree/webhook', '\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook' ); @@ -491,10 +493,10 @@ By default, this controller will automatically handle cancelling subscriptions t #### Webhooks & CSRF Protection -Since Braintree webhooks need to bypass Laravel's [CSRF protection](/docs/{{version}}/routing#csrf-protection), be sure to list the URI as an exception in your `VerifyCsrfToken` middleware or list the route outside of the `web` middleware group: +Since Braintree webhooks need to bypass Laravel's [CSRF protection](/docs/{{version}}/csrf), be sure to list the URI as an exception in your `VerifyCsrfToken` middleware or list the route outside of the `web` middleware group: protected $except = [ - 'stripe/*', + 'braintree/*', ]; diff --git a/blade.md b/blade.md index 0ae3537..ebd3e10 100644 --- a/blade.md +++ b/blade.md @@ -4,6 +4,7 @@ - [Template Inheritance](#template-inheritance) - [Defining A Layout](#defining-a-layout) - [Extending A Layout](#extending-a-layout) +- [Components & Slots](#components-and-slots) - [Displaying Data](#displaying-data) - [Blade & JavaScript Frameworks](#blade-and-javascript-frameworks) - [Control Structures](#control-structures) @@ -11,6 +12,7 @@ - [Loops](#loops) - [The Loop Variable](#the-loop-variable) - [Comments](#comments) + - [PHP](#php) - [Including Sub-Views](#including-sub-views) - [Rendering Views For Collections](#rendering-views-for-collections) - [Stacks](#stacks) @@ -80,6 +82,51 @@ Blade views may be returned from routes using the global `view` helper: return view('child'); }); + +## Components & Slots + +Components and slots provide similar benefits to sections and layouts; however, some may find the mental model of components and slots easier to understand. First, let's imagine a reusable "alert" component we would like to reuse throughout our application: + + + +
+ {{ $slot }} +
+ +The `{{ $slot }}` variable will contain the content we wish to inject into the component. Now, to construct this component, we can use the `@component` Blade directive: + + @component('alert') + Whoops! Something went wrong! + @endcomponent + +Sometimes it is helpful to define multiple slots for a component. Let's modify our alert component to allow for the injection of a "title". Named slots may be displayed by simply "echoing" the variable that matches their name: + + + +
+
{{ $title }}
+ + {{ $slot }} +
+ +Now, we can inject content into the named slot using the `@slot` directive. Any content not within a `@slot` directive will be passed to the component in the `$slot` variable: + + @component('alert') + @slot('title') + Forbidden + @endslot + + You are not allowed to access this resource! + @endcomponent + +#### Passing Additional Data To Components + +Sometimes you may need to pass additional data to a component. For this reason, you can pass an array of data as the second argument to the `@component` directive. All of the data will be made available to the component template as variables: + + @component('alert', ['foo' => 'bar']) + ... + @endcomponent + ## Displaying Data @@ -105,7 +152,7 @@ Sometimes you may wish to echo a variable, but you aren't sure if the variable h {{ isset($name) ? $name : 'Default' }} -However, instead of writing a ternary statement, Blade provides you with the following convenient short-cut, which will be compiled to the ternary statement above: +However, instead of writing a ternary statement, Blade provides you with the following convenient shortcut, which will be compiled to the ternary statement above: {{ $name or 'Default' }} @@ -143,7 +190,7 @@ If you are displaying JavaScript variables in a large portion of your template, ## Control Structures -In addition to template inheritance and displaying data, Blade also provides convenient short-cuts for common PHP control structures, such as conditional statements and loops. These short-cuts provide a very clean, terse way of working with PHP control structures, while also remaining familiar to their PHP counterparts. +In addition to template inheritance and displaying data, Blade also provides convenient shortcuts for common PHP control structures, such as conditional statements and loops. These shortcuts provide a very clean, terse way of working with PHP control structures, while also remaining familiar to their PHP counterparts. ### If Statements @@ -260,6 +307,17 @@ Blade also allows you to define comments in your views. However, unlike HTML com {{-- This comment will not be present in the rendered HTML --}} + +### PHP + +In some situations, it's useful to embed PHP code into your views. You can use the Blade `@php` directive to execute a block of plain PHP within your template: + + @php + // + @endphp + +> {tip} While Blade provides this feature, using it frequently may be a signal that you have too much logic embedded within your template. + ## Including Sub-Views @@ -277,6 +335,10 @@ Even though the included view will inherit all data available in the parent view @include('view.name', ['some' => 'data']) +Of course, if you attempt to `@include` a view which does not exist, Laravel will throw an error. If you would like to include a view that may or may not be present, you should use the `@includeIf` directive: + + @includeIf('view.name', ['some' => 'data']) + > {note} You should avoid using the `__DIR__` and `__FILE__` constants in your Blade views, since they will refer to the location of the cached, compiled view. @@ -343,8 +405,8 @@ The following example creates a `@datetime($var)` directive which formats a give */ public function boot() { - Blade::directive('datetime', function($expression) { - return "format('m/d/Y H:i'); ?>"; + Blade::directive('datetime', function ($expression) { + return "format('m/d/Y H:i'); ?>"; }); } @@ -361,6 +423,6 @@ The following example creates a `@datetime($var)` directive which formats a give As you can see, we will chain the `format` method onto whatever expression is passed into the directive. So, in this example, the final PHP generated by this directive will be: - format('m/d/Y H:i'); ?> + format('m/d/Y H:i'); ?> > {note} After updating the logic of a Blade directive, you will need to delete all of the cached Blade views. The cached Blade views may be removed using the `view:clear` Artisan command. diff --git a/broadcasting.md b/broadcasting.md index 01d92ab..596fff8 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -4,8 +4,9 @@ - [Configuration](#configuration) - [Driver Prerequisites](#driver-prerequisites) - [Concept Overview](#concept-overview) - - [Using Example Application](#using-example-application) + - [Using An Example Application](#using-example-application) - [Defining Broadcast Events](#defining-broadcast-events) + - [Broadcast Name](#broadcast-name) - [Broadcast Data](#broadcast-data) - [Broadcast Queue](#broadcast-queue) - [Authorizing Channels](#authorizing-channels) @@ -16,17 +17,19 @@ - [Receiving Broadcasts](#receiving-broadcasts) - [Installing Laravel Echo](#installing-laravel-echo) - [Listening For Events](#listening-for-events) + - [Leaving A Channel](#leaving-a-channel) - [Namespaces](#namespaces) - [Presence Channels](#presence-channels) - [Authorizing Presence Channels](#authorizing-presence-channels) - [Joining Presence Channels](#joining-presence-channels) - [Broadcasting To Presence Channels](#broadcasting-to-presence-channels) +- [Client Events](#client-events) - [Notifications](#notifications) ## Introduction -In many modern web applications, WebSockets are used to implement real-time, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a WebSocket connection to be handled by the client. This provides a more robust, efficient alternative to continually polling your application for changes. +In many modern web applications, WebSockets are used to implement realtime, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a WebSocket connection to be handled by the client. This provides a more robust, efficient alternative to continually polling your application for changes. To assist you in building these types of applications, Laravel makes it easy to "broadcast" your [events](/docs/{{version}}/events) over a WebSocket connection. Broadcasting your Laravel events allows you to share the same event names between your server-side code and your client-side JavaScript application. @@ -56,9 +59,14 @@ If you are broadcasting your events over [Pusher](https://pusher.com), you shoul composer require pusher/pusher-php-server -Next, you should configure your Pusher credentials in the `config/broadcasting.php` configuration file. An example Pusher configuration is already included in this file, allowing you to quickly specify your Pusher key, secret, and application ID. +Next, you should configure your Pusher credentials in the `config/broadcasting.php` configuration file. An example Pusher configuration is already included in this file, allowing you to quickly specify your Pusher key, secret, and application ID. The `config/broadcasting.php` file's `pusher` configuration also allows you to specify additional `options` that are supported by Pusher, such as the cluster: -When using Pusher and [Laravel Echo](#installing-laravel-echo), you should specify `pusher` as your desired broadcaster when instantiating an Echo instance: + 'options' => [ + 'cluster' => 'eu', + 'encrypted' => true + ], + +When using Pusher and [Laravel Echo](#installing-laravel-echo), you should specify `pusher` as your desired broadcaster when instantiating the Echo instance in your `resources/assets/js/bootstrap.js` file: import Echo from "laravel-echo" @@ -79,19 +87,17 @@ When the Redis broadcaster publishes an event, it will be published on the event #### Socket.IO -> {note} Socket.IO server support is currently in beta and is community driven. Public and private channels are supported; however, presence channels are not fully implemented. - -If you are going to pair the Redis broadcaster with a Socket.IO server, you will need to include the Socket.IO JavaScript client library in your application's `head` HTML element: +If you are going to pair the Redis broadcaster with a Socket.IO server, you will need to include the Socket.IO JavaScript client library in your application's `head` HTML element. When the Socket.IO server is started, it will automatically expose the client JavaScript library at a standard URL. For example, if you are running the Socket.IO server on the same domain as your web application, you may access the client library like so: - + -Next, you will need to instantiate Echo with the `socket.io` connector and a `host`. For example, if your application and socket server are running on the `app.dev` domain you should instantiate Echo like so: +Next, you will need to instantiate Echo with the `socket.io` connector and a `host`. import Echo from "laravel-echo" window.Echo = new Echo({ broadcaster: 'socket.io', - host: 'http://app.dev:6001' + host: window.location.hostname + ':6001' }); Finally, you will need to run a compatible Socket.IO server. Laravel does not include a Socket.IO server implementation; however, a community driven Socket.IO server is currently maintained at the [tlaverdure/laravel-echo-server](https://github.com/tlaverdure/laravel-echo-server) GitHub repository. @@ -103,14 +109,14 @@ Before broadcasting events, you will also need to configure and run a [queue lis ## Concept Overview -Laravel's event broadcasting allows you to broadcast your server-side Laravel events to your client-side JavaScript application using a driver-based approach to WebSockets. Currently, Laravel ships with [Pusher](http://pusher.com) and Redis drivers. The events may be easily consumed on the client-side using the [Laravel Echo](#installing-laravel-echo) Javascript package. +Laravel's event broadcasting allows you to broadcast your server-side Laravel events to your client-side JavaScript application using a driver-based approach to WebSockets. Currently, Laravel ships with [Pusher](https://pusher.com) and Redis drivers. The events may be easily consumed on the client-side using the [Laravel Echo](#installing-laravel-echo) Javascript package. Events are broadcast over "channels", which may be specified as public or private. Any visitor to your application may subscribe to a public channel without any authentication or authorization; however, in order to subscribe to a private channel, a user must be authenticated and authorized to listen on that channel. -### Using Example Application +### Using An Example Application -Before diving into each component of event broadcasting, let's take a high level overview using an e-commerce store as an example. We won't discuss the details of configuring [Pusher](http://pusher.com) or [Laravel Echo](#echo) since that will be discussed in detail in other sections of this documentation. +Before diving into each component of event broadcasting, let's take a high level overview using an e-commerce store as an example. We won't discuss the details of configuring [Pusher](https://pusher.com) or [Laravel Echo](#installing-laravel-echo) since that will be discussed in detail in other sections of this documentation. In our application, let's assume we have a page that allows users to view the shipping status for their orders. Let's also assume that a `ShippingStatusUpdated` event is fired when a shipping status update is processed by the application: @@ -133,7 +139,12 @@ When a user is viewing one of their orders, we don't want them to have to refres class ShippingStatusUpdated implements ShouldBroadcast { - // + /** + * Information about the shipping status update. + * + * @var string + */ + public $update; } The `ShouldBroadcast` interface requires our event to define a `broadcastOn` method. This method is responsible for returning the channels that the event should broadcast on. An empty stub of this method is already defined on generated event classes, so we only need to fill in its details. We only want the creator of the order to be able to view status updates, so we will broadcast the event on a private channel that is tied to the order: @@ -150,21 +161,21 @@ The `ShouldBroadcast` interface requires our event to define a `broadcastOn` met #### Authorizing Channels -Remember, users must be authorized to listen on private channels. We may define our channel authorization rules in the `boot` method of the `BroadcastServiceProvider`. In this example, we need to verify that any user attempting to listen on the private `order.1` channel is actually the creator of the order: +Remember, users must be authorized to listen on private channels. We may define our channel authorization rules in the `routes/channels.php` file. In this example, we need to verify that any user attempting to listen on the private `order.1` channel is actually the creator of the order: - Broadcast::channel('order.*', function ($user, $orderId) { + Broadcast::channel('order.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; }); The `channel` method accepts two arguments: the name of the channel and a callback which returns `true` or `false` indicating whether the user is authorized to listen on the channel. -All authorization callbacks receive the currently authenticated user as their first argument and any additional wildcard parameters as their subsequent arguments. In this example, we are using the `*` character to indicate that the "ID" portion of the channel name is a wildcard. +All authorization callbacks receive the currently authenticated user as their first argument and any additional wildcard parameters as their subsequent arguments. In this example, we are using the `{orderId}` placeholder to indicate that the "ID" portion of the channel name is a wildcard. #### Listening For Event Broadcasts Next, all that remains is to listen for the event in our JavaScript application. We can do this using Laravel Echo. First, we'll use the `private` method to subscribe to the private channel. Then, we may use the `listen` method to listen for the `ShippingStatusUpdated` event. By default, all of the event's public properties will be included on the broadcast event: - Echo.private('order.' + orderId) + Echo.private(`order.${orderId}`) .listen('ShippingStatusUpdated', (e) => { console.log(e.update); }); @@ -174,7 +185,7 @@ Next, all that remains is to listen for the event in our JavaScript application. To inform Laravel that a given event should be broadcast, implement the `Illuminate\Contracts\Broadcasting\ShouldBroadcast` interface on the event class. This interface is already imported into all event classes generated by the framework so you may easily add it to any of your events. -The `ShouldBroadcast` interface requires you to implement a single method: `broadcastOn`. The `broadcastOn` method should return a channel or array of channels that the event should broadcast on. The channels should be instances of `Channel`, `PrivateChannel`, or `PresenceChannel`. Instances of `Channel` represent public channels that any user my subscribe to, while `PrivateChannels` and `PresenceChannels` represent private channels that require [channel authorization](#authorizing-channels): +The `ShouldBroadcast` interface requires you to implement a single method: `broadcastOn`. The `broadcastOn` method should return a channel or array of channels that the event should broadcast on. The channels should be instances of `Channel`, `PrivateChannel`, or `PresenceChannel`. Instances of `Channel` represent public channels that any user may subscribe to, while `PrivateChannels` and `PresenceChannels` represent private channels that require [channel authorization](#authorizing-channels): +### Broadcast Name + +By default, Laravel will broadcast the event using the event's class name. However, you may customize the broadcast name by defining a `broadcastAs` method on the event: + + /** + * The event's broadcast name. + * + * @return string + */ + public function broadcastAs() + { + return 'server.created'; + } + ### Broadcast Data @@ -272,15 +298,25 @@ The `Broadcast::routes` method will automatically place its routes within the `w ### Defining Authorization Callbacks -Next, we need to define the logic that will actually perform the channel authorization. Like defining the authorization routes, this is also done in the `boot` method of the `BroadcastServiceProvider`. In this method, you may use the `Broadcast::channel` method to register channel authorization callbacks: +Next, we need to define the logic that will actually perform the channel authorization. This is done in the `routes/channels.php` file that is included with your application. In this file, you may use the `Broadcast::channel` method to register channel authorization callbacks: - Broadcast::channel('order.*', function ($user, $orderId) { + Broadcast::channel('order.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; }); The `channel` method accepts two arguments: the name of the channel and a callback which returns `true` or `false` indicating whether the user is authorized to listen on the channel. -All authorization callbacks receive the currently authenticated user as their first argument and any additional wildcard parameters as their subsequent arguments. In this example, we are using the `*` character to indicate that the "ID" portion of the channel name is a wildcard. +All authorization callbacks receive the currently authenticated user as their first argument and any additional wildcard parameters as their subsequent arguments. In this example, we are using the `{orderId}` placeholder to indicate that the "ID" portion of the channel name is a wildcard. + +#### Authorization Callback Model Binding + +Just like HTTP routes, channel routes may also take advantage of implicit and explicit [route model binding](/docs/{{version}}/routing#route-model-binding). For example, instead of receiving the string or numeric order ID, you may request an actual `Order` model instance: + + use App\Order; + + Broadcast::channel('order.{order}', function ($user, Order $order) { + return $user->id === $order->user_id; + }); ## Broadcasting Events @@ -302,7 +338,7 @@ However, the `broadcast` function also exposes the `toOthers` method which allow To better understand when you may want to use the `toOthers` method, let's imagine a task list application where a user may create a new task by entering a task name. To create a task, your application might make a request to a `/task` end-point which broadcasts the task's creation and returns a JSON representation of the new task. When your JavaScript application receives the response from the end-point, it might directly insert the new task into its task list like so: - this.$http.post('/task', task) + axios.post('/task', task) .then((response) => { this.tasks.push(response.data); }); @@ -313,9 +349,9 @@ You may solve this by using the `toOthers` method to instruct the broadcaster to #### Configuration -When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. If you are using [Vue](https://vuejs.org) and Vue Resource, the socket ID will automatically be attached to every outgoing request as a `X-Socket-ID` header. Then, when you call the `toOthers` method, Laravel will extract the socket ID from the header and instruct the broadcaster to not broadcast to any connections with that socket ID. +When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. If you are using [Vue](https://vuejs.org) and [Axios](https://github.com/mzabriskie/axios), the socket ID will automatically be attached to every outgoing request as a `X-Socket-ID` header. Then, when you call the `toOthers` method, Laravel will extract the socket ID from the header and instruct the broadcaster to not broadcast to any connections with that socket ID. -If you are not using Vue and Vue Resource, you will need to manually configure your JavaScript application to send the `X-Socket-ID` header. You may retrieve the socket ID using the `Echo.socketId` method: +If you are not using Vue and Axios, you will need to manually configure your JavaScript application to send the `X-Socket-ID` header. You may retrieve the socket ID using the `Echo.socketId` method: var socketId = Echo.socketId(); @@ -338,6 +374,15 @@ Once Echo is installed, you are ready to create a fresh Echo instance in your ap key: 'your-pusher-key' }); +When creating an Echo instance that uses the `pusher` connector, you may also specify a `cluster` as well as whether the connection should be encrypted: + + window.Echo = new Echo({ + broadcaster: 'pusher', + key: 'your-pusher-key', + cluster: 'eu', + encrypted: true + }); + ### Listening For Events @@ -355,6 +400,13 @@ If you would like to listen for events on a private channel, use the `private` m .listen(...) .listen(...); + +### Leaving A Channel + +To leave a channel, you may call the `leave` method on your Echo instance: + + Echo.leave('orders'); + ### Namespaces @@ -396,7 +448,7 @@ The data returned by the authorization callback will be made available to the pr To join a presence channel, you may use Echo's `join` method. The `join` method will return a `PresenceChannel` implementation which, along with exposing the `listen` method, allows you to subscribe to the `here`, `joining`, and `leaving` events. - Echo.join('chat.' + roomId) + Echo.join(`chat.${roomId}`) .here((users) => { // }) @@ -432,7 +484,7 @@ Like public or private events, presence channel events may be broadcast using th You may listen for the join event via Echo's `listen` method: - Echo.join('chat.' + roomId) + Echo.join(`chat.${roomId}`) .here(...) .joining(...) .leaving(...) @@ -440,6 +492,23 @@ You may listen for the join event via Echo's `listen` method: // }); + +## Client Events + +Sometimes you may wish to broadcast an event to other connected clients without hitting your Laravel application at all. This can be particularly useful for things like "typing" notifications, where you want to alert users of your application that another user is typing a message on a given screen. To broadcast client events, you may use Echo's `whisper` method: + + Echo.channel('chat') + .whisper('typing', { + name: this.user.name + }); + +To listen for client events, you may use the `listenForWhisper` method: + + Echo.channel('chat') + .listenForWhisper('typing', (e) => { + console.log(e.name); + }); + ## Notifications @@ -447,9 +516,9 @@ By pairing event broadcasting with [notifications](/docs/{{version}}/notificatio Once you have configured a notification to use the broadcast channel, you may listen for the broadcast events using Echo's `notification` method. Remember, the channel name should match the class name of the entity receiving the notifications: - Echo.private('App.User.' + userId) + Echo.private(`App.User.${userId}`) .notification((notification) => { console.log(notification.type); }); -In this example, all notifications sent to `App\User` instances via the `broadcast` channel would be received by the callback. A channel authorization callback for the `App.User.*` channel is included in the default `BroadcastServiceProvider` that ships with the Laravel framework. +In this example, all notifications sent to `App\User` instances via the `broadcast` channel would be received by the callback. A channel authorization callback for the `App.User.{id}` channel is included in the default `BroadcastServiceProvider` that ships with the Laravel framework. diff --git a/cache.md b/cache.md index d3039f0..199f92d 100644 --- a/cache.md +++ b/cache.md @@ -7,6 +7,7 @@ - [Retrieving Items From The Cache](#retrieving-items-from-the-cache) - [Storing Items In The Cache](#storing-items-in-the-cache) - [Removing Items From The Cache](#removing-items-from-the-cache) + - [The Cache Helper](#the-cache-helper) - [Cache Tags](#cache-tags) - [Storing Tagged Cache Items](#storing-tagged-cache-items) - [Accessing Tagged Cache Items](#accessing-tagged-cache-items) @@ -19,7 +20,7 @@ ## Configuration -Laravel provides an expressive, unified API for various caching backends. The cache configuration is located at `config/cache.php`. In this file you may specify which cache driver you would like used by default throughout your application. Laravel supports popular caching backends like [Memcached](http://memcached.org) and [Redis](http://redis.io) out of the box. +Laravel provides an expressive, unified API for various caching backends. The cache configuration is located at `config/cache.php`. In this file you may specify which cache driver you would like used by default throughout your application. Laravel supports popular caching backends like [Memcached](https://memcached.org) and [Redis](http://redis.io) out of the box. The cache configuration file also contains various other options, which are documented within the file, so make sure to read over these options. By default, Laravel is configured to use the `file` cache driver, which stores the serialized, cached objects in the filesystem. For larger applications, it is recommended that you use a more robust driver such as Memcached or Redis. You may even configure multiple cache configurations for the same driver. @@ -30,7 +31,7 @@ The cache configuration file also contains various other options, which are docu When using the `database` cache driver, you will need to setup a table to contain the cache items. You'll find an example `Schema` declaration for the table below: - Schema::create('cache', function($table) { + Schema::create('cache', function ($table) { $table->string('key')->unique(); $table->text('value'); $table->integer('expiration'); @@ -40,7 +41,7 @@ When using the `database` cache driver, you will need to setup a table to contai #### Memcached -Using the Memcached driver requires the [Memcached PECL package](http://pecl.php.net/package/memcached) to be installed. You may list all of your Memcached servers in the `config/cache.php` configuration file: +Using the Memcached driver requires the [Memcached PECL package](https://pecl.php.net/package/memcached) to be installed. You may list all of your Memcached servers in the `config/cache.php` configuration file: 'memcached' => [ [ @@ -62,7 +63,7 @@ You may also set the `host` option to a UNIX socket path. If you do this, the `p #### Redis -Before using a Redis cache with Laravel, you will need to install the `predis/predis` package (~1.0) via Composer. +Before using a Redis cache with Laravel, you will need to either install the `predis/predis` package (~1.0) via Composer or install the PhpRedis PHP extension via PECL. For more information on configuring Redis, consult its [Laravel documentation page](/docs/{{version}}/redis#configuration). @@ -114,16 +115,15 @@ The `get` method on the `Cache` facade is used to retrieve items from the cache. $value = Cache::get('key', 'default'); - You may even pass a `Closure` as the default value. The result of the `Closure` will be returned if the specified item does not exist in the cache. Passing a Closure allows you to defer the retrieval of default values from a database or other external service: - $value = Cache::get('key', function() { + $value = Cache::get('key', function () { return DB::table(...)->get(); }); #### Checking For Item Existence -The `has` method may be used to determine if an item exists in the cache: +The `has` method may be used to determine if an item exists in the cache. This method will return `false` if the value is `null` or `false`: if (Cache::has('key')) { // @@ -142,7 +142,7 @@ The `increment` and `decrement` methods may be used to adjust the value of integ Sometimes you may wish to retrieve an item from the cache, but also store a default value if the requested item doesn't exist. For example, you may wish to retrieve all users from the cache or, if they don't exist, retrieve them from the database and add them to the cache. You may do this using the `Cache::remember` method: - $value = Cache::remember('users', $minutes, function() { + $value = Cache::remember('users', $minutes, function () { return DB::table('users')->get(); }); @@ -194,6 +194,21 @@ You may clear the entire cache using the `flush` method: > {note} Flushing the cache does not respect the cache prefix and will remove all entries from the cache. Consider this carefully when clearing a cache which is shared by other applications. + +### The Cache Helper + +In addition to using the `Cache` facade or [cache contract](/docs/{{version}}/contracts), you may also use the global `cache` function to retrieve and store data via the cache. When the `cache` function is called with a single, string argument, it will return the value of the given key: + + $value = cache('key'); + +If you provide an array of key / value pairs and an expiration time to the function, it will store values in the cache for the specified duration: + + cache(['key' => 'value'], $minutes); + + cache(['key' => 'value'], Carbon::now()->addSeconds(10)); + +> {tip} When testing call to the global `cache` function, you may use the `Cache::shouldReceive` method just as if you were [testing a facade](/docs/{{version}}/mocking#mocking-facades). + ## Cache Tags @@ -204,16 +219,16 @@ You may clear the entire cache using the `flush` method: Cache tags allow you to tag related items in the cache and then flush all cached values that have been assigned a given tag. You may access a tagged cache by passing in an ordered array of tag names. For example, let's access a tagged cache and `put` value in the cache: - Cache::tags(['people', 'artists'])->put('John', $john, $minutes); + Cache::tags(['people', 'artists'])->put('John', $john, $minutes); - Cache::tags(['people', 'authors'])->put('Anne', $anne, $minutes); + Cache::tags(['people', 'authors'])->put('Anne', $anne, $minutes); ### Accessing Tagged Cache Items To retrieve a tagged cache item, pass the same ordered list of tags to the `tags` method and then call the `get` method with the key you wish to retrieve: - $john = Cache::tags(['people', 'artists'])->get('John'); + $john = Cache::tags(['people', 'artists'])->get('John'); $anne = Cache::tags(['people', 'authors'])->get('Anne'); @@ -222,11 +237,11 @@ To retrieve a tagged cache item, pass the same ordered list of tags to the `tags You may flush all items that are assigned a tag or list of tags. For example, this statement would remove all caches tagged with either `people`, `authors`, or both. So, both `Anne` and `John` would be removed from the cache: - Cache::tags(['people', 'authors'])->flush(); + Cache::tags(['people', 'authors'])->flush(); In contrast, this statement would remove only caches tagged with `authors`, so `Anne` would be removed, but not `John`: - Cache::tags('authors')->flush(); + Cache::tags('authors')->flush(); ## Adding Custom Cache Drivers @@ -258,7 +273,7 @@ To create our custom cache driver, we first need to implement the `Illuminate\Co We just need to implement each of these methods using a MongoDB connection. For an example of how to implement each of these methods, take a look at the `Illuminate\Cache\MemcachedStore` in the framework source code. Once our implementation is complete, we can finish our custom driver registration. - Cache::extend('mongo', function($app) { + Cache::extend('mongo', function ($app) { return Cache::repository(new MongoStore); }); @@ -286,7 +301,7 @@ To register the custom cache driver with Laravel, we will use the `extend` metho */ public function boot() { - Cache::extend('mongo', function($app) { + Cache::extend('mongo', function ($app) { return Cache::repository(new MongoStore); }); } diff --git a/collections.md b/collections.md index 808aa49..e71060b 100644 --- a/collections.md +++ b/collections.md @@ -3,6 +3,7 @@ - [Introduction](#introduction) - [Creating Collections](#creating-collections) - [Available Methods](#available-methods) +- [Higher Order Messages](#higher-order-messages) ## Introduction @@ -75,10 +76,14 @@ For the remainder of this documentation, we'll discuss each method available on [keys](#method-keys) [last](#method-last) [map](#method-map) +[mapWithKeys](#method-mapwithkeys) [max](#method-max) [merge](#method-merge) [min](#method-min) +[nth](#method-nth) [only](#method-only) +[partition](#method-partition) +[pipe](#method-pipe) [pluck](#method-pluck) [pop](#method-pop) [prepend](#method-prepend) @@ -97,6 +102,7 @@ For the remainder of this documentation, we'll discuss each method available on [sortBy](#method-sortby) [sortByDesc](#method-sortbydesc) [splice](#method-splice) +[split](#method-split) [sum](#method-sum) [take](#method-take) [toArray](#method-toarray) @@ -108,7 +114,7 @@ For the remainder of this documentation, we'll discuss each method available on [where](#method-where) [whereStrict](#method-wherestrict) [whereIn](#method-wherein) -[whereInLoose](#method-whereinloose) +[whereInStrict](#method-whereinstrict) [zip](#method-zip) @@ -168,7 +174,7 @@ The `chunk` method breaks the collection into multiple, smaller collections of a // [[1, 2, 3, 4], [5, 6, 7]] -This method is especially useful in [views](/docs/{{version}}/views) when working with a grid system such as [Bootstrap](http://getbootstrap.com/css/#grid). Imagine you have a collection of [Eloquent](/docs/{{version}}/eloquent) models you want to display in a grid: +This method is especially useful in [views](/docs/{{version}}/views) when working with a grid system such as [Bootstrap](https://getbootstrap.com/css/#grid). Imagine you have a collection of [Eloquent](/docs/{{version}}/eloquent) models you want to display in a grid: @foreach ($products->chunk(3) as $chunk)
@@ -308,19 +314,13 @@ If you would like to stop iterating through the items, you may return `false` fr #### `every()` {#collection-method} -The `every` method creates a new collection consisting of every n-th element: +The `every` method may be used to verify that all elements of a collection pass a given truth test: - $collection = collect(['a', 'b', 'c', 'd', 'e', 'f']); - - $collection->every(4); - - // ['a', 'e'] - -You may optionally pass an offset as the second argument: - - $collection->every(4, 1); + collect([1, 2, 3, 4])->every(function ($value, $key) { + return $value > 2; + }); - // ['b', 'f'] + // false #### `except()` {#collection-method} @@ -352,6 +352,14 @@ The `filter` method filters the collection using the given callback, keeping onl // [3, 4] +If no callback is supplied, all entries of the collection that are equivalent to `false` will be removed: + + $collection = collect([1, 2, 3, null, false, '', 0, []]); + + $collection->filter()->all(); + + // [1, 2, 3] + For the inverse of `filter`, see the [reject](#method-reject) method. @@ -549,9 +557,9 @@ The `has` method determines if a given key exists in the collection: $collection = collect(['account_id' => 1, 'product' => 'Desk']); - $collection->has('email'); + $collection->has('product'); - // false + // true #### `implode()` {#collection-method} @@ -682,10 +690,41 @@ The `map` method iterates through the collection and passes each value to the gi > {note} Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. + +#### `mapWithKeys()` {#collection-method} + +The `mapWithKeys` method iterates through the collection and passes each value to the given callback. The callback should return an associative array containing a single key / value pair: + + $collection = collect([ + [ + 'name' => 'John', + 'department' => 'Sales', + 'email' => 'john@example.com' + ], + [ + 'name' => 'Jane', + 'department' => 'Marketing', + 'email' => 'jane@example.com' + ] + ]); + + $keyed = $collection->mapWithKeys(function ($item) { + return [$item['email'] => $item['name']]; + }); + + $keyed->all(); + + /* + [ + 'john@example.com' => 'John', + 'jane@example.com' => 'Jane', + ] + */ + #### `max()` {#collection-method} -The `max` method return the maximum value of a given key: +The `max` method returns the maximum value of a given key: $max = collect([['foo' => 10], ['foo' => 20]])->max('foo'); @@ -698,7 +737,7 @@ The `max` method return the maximum value of a given key: #### `merge()` {#collection-method} -The `merge` method merges the given array into the original collection. If a string key in the given array matches a string key in the original collection, the given array's value will overwrite the value in the original collection: +The `merge` method merges the given array with the original collection. If a string key in the given array matches a string key in the original collection, the given array's value will overwrite the value in the original collection: $collection = collect(['product_id' => 1, 'price' => 100]); @@ -706,7 +745,7 @@ The `merge` method merges the given array into the original collection. If a str $merged->all(); - // ['product_id' => 1, price' => 200, 'discount' => false] + // ['product_id' => 1, 'price' => 200, 'discount' => false] If the given array's keys are numeric, the values will be appended to the end of the collection: @@ -721,7 +760,7 @@ If the given array's keys are numeric, the values will be appended to the end of #### `min()` {#collection-method} -The `min` method return the minimum value of a given key: +The `min` method returns the minimum value of a given key: $min = collect([['foo' => 10], ['foo' => 20]])->min('foo'); @@ -731,6 +770,23 @@ The `min` method return the minimum value of a given key: // 1 + +#### `nth()` {#collection-method} + +The `nth` method creates a new collection consisting of every n-th element: + + $collection = collect(['a', 'b', 'c', 'd', 'e', 'f']); + + $collection->nth(4); + + // ['a', 'e'] + +You may optionally pass an offset as the second argument: + + $collection->nth(4, 1); + + // ['b', 'f'] + #### `only()` {#collection-method} @@ -746,6 +802,30 @@ The `only` method returns the items in the collection with the specified keys: For the inverse of `only`, see the [except](#method-except) method. + +#### `partition()` {#collection-method} + +The `partition` method may be combined with the `list` PHP function to separate elements that pass a given truth test from those that do not: + + $collection = collect([1, 2, 3, 4, 5, 6]); + + list($underThree, $aboveThree) = $collection->partition(function ($i) { + return $i < 3; + }); + + +#### `pipe()` {#collection-method} + +The `pipe` method passes the collection to the given callback and returns the result: + + $collection = collect([1, 2, 3]); + + $piped = $collection->pipe(function ($collection) { + return $collection->sum(); + }); + + // 6 + #### `pluck()` {#collection-method} @@ -860,7 +940,7 @@ The `random` method returns a random item from the collection: // 4 - (retrieved randomly) -You may optionally pass an integer to `random` to specify how many items you would like to randomly retrieve. If that integer is more than `1`, a collection of items is returned: +You may optionally pass an integer to `random` to specify how many items you would like to randomly retrieve. A collection of items is always returned when explicitly passing the number of items you wish to receive: $random = $collection->random(3); @@ -1008,7 +1088,7 @@ The `sort` method sorts the collection. The sorted collection keeps the original // [1, 2, 3, 4, 5] -If your sorting needs are more advanced, you may pass a callback to `sort` with your own algorithm. Refer to the PHP documentation on [`usort`](http://php.net/manual/en/function.usort.php#refsect1-function.usort-parameters), which is what the collection's `sort` method calls under the hood. +If your sorting needs are more advanced, you may pass a callback to `sort` with your own algorithm. Refer to the PHP documentation on [`usort`](https://secure.php.net/manual/en/function.usort.php#refsect1-function.usort-parameters), which is what the collection's `sort` method calls under the hood. > {tip} If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. @@ -1107,6 +1187,19 @@ In addition, you can pass a third argument containing the new items to replace t // [1, 2, 10, 11, 4, 5] + +#### `split()` {#collection-method} + +The `split` method breaks a collection into the given number of groups: + + $collection = collect([1, 2, 3, 4, 5]); + + $groups = $collection->split(3); + + $groups->toArray(); + + // [[1, 2], [3, 4], [5]] + #### `sum()` {#collection-method} @@ -1190,7 +1283,7 @@ The `toJson` method converts the collection into JSON: $collection->toJson(); - // '{"name":"Desk","price":200}' + // '{"name":"Desk", "price":200}' #### `transform()` {#collection-method} @@ -1220,7 +1313,7 @@ The `union` method adds the given array to the collection. If the given array co $union->all(); - // [1 => ['a'], 2 => ['b'], [3 => ['c']] + // [1 => ['a'], 2 => ['b'], 3 => ['c']] #### `unique()` {#collection-method} @@ -1346,12 +1439,12 @@ The `whereIn` method filters the collection by a given key / value contained wit ] */ -The `whereIn` method uses strict comparisons when checking item values. Use the [`whereInLoose`](#method-whereinloose) method to filter using "loose" comparisons. +The `whereIn` method uses "loose" comparisons when checking item values. Use the [`whereInStrict`](#method-whereinstrict) method to filter using strict comparisons. - -#### `whereInLoose()` {#collection-method} + +#### `whereInStrict()` {#collection-method} -This method has the same signature as the [`whereIn`](#method-wherein) method; however, all values are compared using "loose" comparisons. +This method has the same signature as the [`whereIn`](#method-wherein) method; however, all values are compared using strict comparisons. #### `zip()` {#collection-method} @@ -1365,3 +1458,20 @@ The `zip` method merges together the values of the given array with the values o $zipped->all(); // [['Chair', 100], ['Desk', 200]] + + +## Higher Order Messages + +Collections also provide support for "higher order messages", which are short-cuts for performing common actions on collections. The collection methods that provide higher order messages are: `contains`, `each`, `every`, `filter`, `first`, `map`, `partition`, `reject`, `sortBy`, `sortByDesc`, and `sum`. + +Each higher order message can be accessed as a dynamic property on a collection instance. For instance, let's use the `each` higher order message to call a method on each object within a collection: + + $users = User::where('votes', '>', 500)->get(); + + $users->each->markAsVip(); + +Likewise, we can use the `sum` higher order message to gather the total number of "votes" for a collection of users: + + $users = User::where('group', 'Development')->get(); + + return $users->sum->votes; diff --git a/configuration.md b/configuration.md index d681f80..a72764b 100644 --- a/configuration.md +++ b/configuration.md @@ -1,9 +1,9 @@ # Configuration - [Introduction](#introduction) -- [Accessing Configuration Values](#accessing-configuration-values) - [Environment Configuration](#environment-configuration) - [Determining The Current Environment](#determining-the-current-environment) +- [Accessing Configuration Values](#accessing-configuration-values) - [Configuration Caching](#configuration-caching) - [Maintenance Mode](#maintenance-mode) @@ -12,17 +12,6 @@ All of the configuration files for the Laravel framework are stored in the `config` directory. Each option is documented, so feel free to look through the files and get familiar with the options available to you. - -## Accessing Configuration Values - -You may easily access your configuration values using the global `config` helper function from anywhere in your application. The configuration values may be accessed using "dot" syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist: - - $value = config('app.timezone'); - -To set configuration values at runtime, pass an array to the `config` helper: - - config(['app.timezone' => 'America/Chicago']); - ## Environment Configuration @@ -30,6 +19,8 @@ It is often helpful to have different configuration values based on the environm To make this a cinch, Laravel utilizes the [DotEnv](https://github.com/vlucas/phpdotenv) PHP library by Vance Lucas. In a fresh Laravel installation, the root directory of your application will contain a `.env.example` file. If you install Laravel via Composer, this file will automatically be renamed to `.env`. Otherwise, you should rename the file manually. +> {tip} You may also create a `.env.testing` file. This file will override values from the `.env` file when running PHPUnit tests or executing Artisan commands with the `--env=testing` option. + #### Retrieving Environment Configuration All of the variables listed in this file will be loaded into the `$_ENV` PHP super-global when your application receives a request. However, you may use the `env` helper to retrieve values from these variables in your configuration files. In fact, if you review the Laravel configuration files, you will notice several of the options already using this helper: @@ -59,6 +50,17 @@ You may also pass arguments to the `environment` method to check if the environm // The environment is either local OR staging... } + +## Accessing Configuration Values + +You may easily access your configuration values using the global `config` helper function from anywhere in your application. The configuration values may be accessed using "dot" syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist: + + $value = config('app.timezone'); + +To set configuration values at runtime, pass an array to the `config` helper: + + config(['app.timezone' => 'America/Chicago']); + ## Configuration Caching @@ -66,6 +68,8 @@ To give your application a speed boost, you should cache all of your configurati You should typically run the `php artisan config:cache` command as part of your production deployment routine. The command should not be run during local development as configuration options will frequently need to be changed during the course of your application's development. +> {note} If you execute the `config:cache` command during your deployment process, you should be sure that you are only calling the `env` function from within your configuration files. + ## Maintenance Mode @@ -77,7 +81,7 @@ To enable maintenance mode, simply execute the `down` Artisan command: You may also provide `message` and `retry` options to the `down` command. The `message` value may be used to display or log a custom message, while the `retry` value will be set as the `Retry-After` HTTP header's value: - php artisan down --message='Upgrading Database' --retry=60 + php artisan down --message="Upgrading Database" --retry=60 To disable maintenance mode, use the `up` command: diff --git a/container.md b/container.md index 3b4bbe1..5431105 100644 --- a/container.md +++ b/container.md @@ -186,9 +186,9 @@ You may use the `make` method to resolve a class instance out of the container. $api = $this->app->make('HelpSpot\API'); -If you are in a location of your code that does not have access to the `$app` variable, you may use the global `app` helper: +If you are in a location of your code that does not have access to the `$app` variable, you may use the global `resolve` helper: - $api = app('HelpSpot\API'); + $api = resolve('HelpSpot\API'); #### Automatic Injection diff --git a/contracts.md b/contracts.md index 8803e40..4d8a820 100644 --- a/contracts.md +++ b/contracts.md @@ -31,7 +31,7 @@ Unlike facades, which do not require you to require them in your class' construc As discussed elsewhere, much of the decision to use contracts or facades will come down to personal taste and the tastes of your development team. Both contracts and facades can be used to create robust, well-tested Laravel applications. As long as you are keeping your class' responsibilities focused, you will notice very few practical differences between using contracts and facades. -However, you may still have several questions regarding contracts. For example, why use interfaces at all? Isn't using interfaces more complicated? Let's distil the reasons for using interfaces to the following headings: loose coupling and simplicity. +However, you may still have several questions regarding contracts. For example, why use interfaces at all? Isn't using interfaces more complicated? Let's distill the reasons for using interfaces to the following headings: loose coupling and simplicity. ### Loose Coupling @@ -170,36 +170,36 @@ This table provides a quick reference to all of the Laravel contracts and their Contract | References Facade ------------- | ------------- -[Illuminate\Contracts\Auth\Factory](https://github.com/illuminate/contracts/blob/master/Auth/Factory.php) | Auth -[Illuminate\Contracts\Auth\PasswordBroker](https://github.com/illuminate/contracts/blob/master/Auth/PasswordBroker.php) | Password -[Illuminate\Contracts\Bus\Dispatcher](https://github.com/illuminate/contracts/blob/master/Bus/Dispatcher.php) | Bus -[Illuminate\Contracts\Broadcasting\Broadcaster](https://github.com/illuminate/contracts/blob/master/Broadcasting/Broadcaster.php) |   -[Illuminate\Contracts\Cache\Repository](https://github.com/illuminate/contracts/blob/master/Cache/Repository.php) | Cache -[Illuminate\Contracts\Cache\Factory](https://github.com/illuminate/contracts/blob/master/Cache/Factory.php) | Cache::driver() -[Illuminate\Contracts\Config\Repository](https://github.com/illuminate/contracts/blob/master/Config/Repository.php) | Config -[Illuminate\Contracts\Container\Container](https://github.com/illuminate/contracts/blob/master/Container/Container.php) | App -[Illuminate\Contracts\Cookie\Factory](https://github.com/illuminate/contracts/blob/master/Cookie/Factory.php) | Cookie -[Illuminate\Contracts\Cookie\QueueingFactory](https://github.com/illuminate/contracts/blob/master/Cookie/QueueingFactory.php) | Cookie::queue() -[Illuminate\Contracts\Encryption\Encrypter](https://github.com/illuminate/contracts/blob/master/Encryption/Encrypter.php) | Crypt -[Illuminate\Contracts\Events\Dispatcher](https://github.com/illuminate/contracts/blob/master/Events/Dispatcher.php) | Event -[Illuminate\Contracts\Filesystem\Cloud](https://github.com/illuminate/contracts/blob/master/Filesystem/Cloud.php) |   -[Illuminate\Contracts\Filesystem\Factory](https://github.com/illuminate/contracts/blob/master/Filesystem/Factory.php) | File -[Illuminate\Contracts\Filesystem\Filesystem](https://github.com/illuminate/contracts/blob/master/Filesystem/Filesystem.php) | File -[Illuminate\Contracts\Foundation\Application](https://github.com/illuminate/contracts/blob/master/Foundation/Application.php) | App -[Illuminate\Contracts\Hashing\Hasher](https://github.com/illuminate/contracts/blob/master/Hashing/Hasher.php) | Hash -[Illuminate\Contracts\Logging\Log](https://github.com/illuminate/contracts/blob/master/Logging/Log.php) | Log -[Illuminate\Contracts\Mail\MailQueue](https://github.com/illuminate/contracts/blob/master/Mail/MailQueue.php) | Mail::queue() -[Illuminate\Contracts\Mail\Mailer](https://github.com/illuminate/contracts/blob/master/Mail/Mailer.php) | Mail -[Illuminate\Contracts\Queue\Factory](https://github.com/illuminate/contracts/blob/master/Queue/Factory.php) | Queue::driver() -[Illuminate\Contracts\Queue\Queue](https://github.com/illuminate/contracts/blob/master/Queue/Queue.php) | Queue -[Illuminate\Contracts\Redis\Database](https://github.com/illuminate/contracts/blob/master/Redis/Database.php) | Redis -[Illuminate\Contracts\Routing\Registrar](https://github.com/illuminate/contracts/blob/master/Routing/Registrar.php) | Route -[Illuminate\Contracts\Routing\ResponseFactory](https://github.com/illuminate/contracts/blob/master/Routing/ResponseFactory.php) | Response -[Illuminate\Contracts\Routing\UrlGenerator](https://github.com/illuminate/contracts/blob/master/Routing/UrlGenerator.php) | URL -[Illuminate\Contracts\Support\Arrayable](https://github.com/illuminate/contracts/blob/master/Support/Arrayable.php) |   -[Illuminate\Contracts\Support\Jsonable](https://github.com/illuminate/contracts/blob/master/Support/Jsonable.php) |   -[Illuminate\Contracts\Support\Renderable](https://github.com/illuminate/contracts/blob/master/Support/Renderable.php) |   -[Illuminate\Contracts\Validation\Factory](https://github.com/illuminate/contracts/blob/master/Validation/Factory.php) | Validator::make() -[Illuminate\Contracts\Validation\Validator](https://github.com/illuminate/contracts/blob/master/Validation/Validator.php) |   -[Illuminate\Contracts\View\Factory](https://github.com/illuminate/contracts/blob/master/View/Factory.php) | View::make() -[Illuminate\Contracts\View\View](https://github.com/illuminate/contracts/blob/master/View/View.php) |   +[Illuminate\Contracts\Auth\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Auth/Factory.php) | Auth +[Illuminate\Contracts\Auth\PasswordBroker](https://github.com/illuminate/contracts/blob/{{version}}/Auth/PasswordBroker.php) | Password +[Illuminate\Contracts\Bus\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Bus/Dispatcher.php) | Bus +[Illuminate\Contracts\Broadcasting\Broadcaster](https://github.com/illuminate/contracts/blob/{{version}}/Broadcasting/Broadcaster.php) |   +[Illuminate\Contracts\Cache\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Repository.php) | Cache +[Illuminate\Contracts\Cache\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cache/Factory.php) | Cache::driver() +[Illuminate\Contracts\Config\Repository](https://github.com/illuminate/contracts/blob/{{version}}/Config/Repository.php) | Config +[Illuminate\Contracts\Container\Container](https://github.com/illuminate/contracts/blob/{{version}}/Container/Container.php) | App +[Illuminate\Contracts\Cookie\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/Factory.php) | Cookie +[Illuminate\Contracts\Cookie\QueueingFactory](https://github.com/illuminate/contracts/blob/{{version}}/Cookie/QueueingFactory.php) | Cookie::queue() +[Illuminate\Contracts\Encryption\Encrypter](https://github.com/illuminate/contracts/blob/{{version}}/Encryption/Encrypter.php) | Crypt +[Illuminate\Contracts\Events\Dispatcher](https://github.com/illuminate/contracts/blob/{{version}}/Events/Dispatcher.php) | Event +[Illuminate\Contracts\Filesystem\Cloud](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Cloud.php) |   +[Illuminate\Contracts\Filesystem\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Factory.php) | File +[Illuminate\Contracts\Filesystem\Filesystem](https://github.com/illuminate/contracts/blob/{{version}}/Filesystem/Filesystem.php) | File +[Illuminate\Contracts\Foundation\Application](https://github.com/illuminate/contracts/blob/{{version}}/Foundation/Application.php) | App +[Illuminate\Contracts\Hashing\Hasher](https://github.com/illuminate/contracts/blob/{{version}}/Hashing/Hasher.php) | Hash +[Illuminate\Contracts\Logging\Log](https://github.com/illuminate/contracts/blob/{{version}}/Logging/Log.php) | Log +[Illuminate\Contracts\Mail\MailQueue](https://github.com/illuminate/contracts/blob/{{version}}/Mail/MailQueue.php) | Mail::queue() +[Illuminate\Contracts\Mail\Mailer](https://github.com/illuminate/contracts/blob/{{version}}/Mail/Mailer.php) | Mail +[Illuminate\Contracts\Queue\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Factory.php) | Queue::driver() +[Illuminate\Contracts\Queue\Queue](https://github.com/illuminate/contracts/blob/{{version}}/Queue/Queue.php) | Queue +[Illuminate\Contracts\Redis\Database](https://github.com/illuminate/contracts/blob/{{version}}/Redis/Database.php) | Redis +[Illuminate\Contracts\Routing\Registrar](https://github.com/illuminate/contracts/blob/{{version}}/Routing/Registrar.php) | Route +[Illuminate\Contracts\Routing\ResponseFactory](https://github.com/illuminate/contracts/blob/{{version}}/Routing/ResponseFactory.php) | Response +[Illuminate\Contracts\Routing\UrlGenerator](https://github.com/illuminate/contracts/blob/{{version}}/Routing/UrlGenerator.php) | URL +[Illuminate\Contracts\Support\Arrayable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Arrayable.php) |   +[Illuminate\Contracts\Support\Jsonable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Jsonable.php) |   +[Illuminate\Contracts\Support\Renderable](https://github.com/illuminate/contracts/blob/{{version}}/Support/Renderable.php) |   +[Illuminate\Contracts\Validation\Factory](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Factory.php) | Validator::make() +[Illuminate\Contracts\Validation\Validator](https://github.com/illuminate/contracts/blob/{{version}}/Validation/Validator.php) |   +[Illuminate\Contracts\View\Factory](https://github.com/illuminate/contracts/blob/{{version}}/View/Factory.php) | View::make() +[Illuminate\Contracts\View\View](https://github.com/illuminate/contracts/blob/{{version}}/View/View.php) |   diff --git a/contributing.md b/contributing.md index e68c3d3..266c057 100644 --- a/contributing.md +++ b/contributing.md @@ -1,3 +1,17 @@ -# Contribution Guidelines +# ပူးပေါင်းပါဝင်မှု လမ်းညွန် -If you are submitting documentation for the **current stable release**, submit it to the corresponding branch. For example, documentation for Laravel 5.1 would be submitted to the `5.1` branch. Documentation intended for the next release of Laravel should be submitted to the `master` branch. \ No newline at end of file +###ဘာသာပြန်ခြင်း + + - ကျွန်တော်တို့ကို ပူးပေါင်းပါဝင် ကူညီပြီးဘာသာပြန်ချင်တယ်ဆိုရင် + [docs](https://github.com/setkyar/laravel-docs/) ကိုဦးစွာ Fork လုပ်ပါ၊ + + - ဘယ်အပိုင်းကို ဘာသာပြန်မည်ဆိုတာကို [Facebook](https://www.facebook.com/groups/250409601822202/) မှာပြောပေးပါ။ (Fork + လုပ်ပြီးဘာသာပြန်နေတုန်းအခြားတစ်ယောက်ယောက်ကပါဘာသာပြန်နေတာမျိုးဖြစ်မှာစိုးလို့ပါ) + + - သင်ဘာသာပြန်မည့် File ကိုဘာသာပြန်ပါ။ ဘာသာပြန်ပြီးရင် မူရင်း repo ဆီက + [pull request](https://github.com/setkyar/laravel-docs/pulls) တောင်းပါ။ (pull request တောင်းတာကို မြန်မြန် accept + လုပ်စေချင်တယ်ဆိုရင်[Facebook]( https://www.facebook.com/groups/250409601822202/) မှာပါတင်ပေးပါ) + +###ဘာသာပြန်အဆင်ပြေမှူနှင့် စာလုံးပေါင်းအမှား + +ဘာသာပြန်ထားတာတွေ ကိုဦးစွာဖတ်ပါ။ ဘာသာပြန်အဆင်ပြေမှူ နဲ့ စာလုံးပေါင်း အမှားတွေကို စစ်ပါ။ ဘာသာပြန်အဆင်ပြေမှူမရှိတာတို့ စာလုံးပေါင်းအမှားတွေတွေ့ရင် GitHub မှာ [issue](https://github.com/setkyar/laravel-docs/issues) တင်ပေးပါ။(အမှားတွေကို အမြန်ဆုံး စစ်ပေးဖို့ [Facebook](https://www.facebook.com/groups/250409601822202/) မှာပါတင်ပေးပါ) diff --git a/contributions.md b/contributions.md index fd80e2c..9c94c09 100644 --- a/contributions.md +++ b/contributions.md @@ -27,6 +27,9 @@ The Laravel source code is managed on Github, and there are repositories for eac - [Laravel Envoy](https://github.com/laravel/envoy) - [Laravel Homestead](https://github.com/laravel/homestead) - [Laravel Homestead Build Scripts](https://github.com/laravel/settler) +- [Laravel Passport](https://github.com/laravel/passport) +- [Laravel Scout](https://github.com/laravel/scout) +- [Laravel Socialite](https://github.com/laravel/socialite) - [Laravel Website](https://github.com/laravel/laravel.com) - [Laravel Art](https://github.com/laravel/art) @@ -35,7 +38,7 @@ The Laravel source code is managed on Github, and there are repositories for eac You may propose new features or improvements of existing Laravel behavior in the Laravel Internals [issue board](https://github.com/laravel/internals/issues). If you propose a new feature, please be willing to implement at least some of the code that would be needed to complete the feature. -Informal discussion regarding bugs, new features, and implementation of existing features takes place in the `#internals` channel of the [LaraChat](http://larachat.co) Slack team. Taylor Otwell, the maintainer of Laravel, is typically present in the channel on weekdays from 8am-5pm (UTC-06:00 or America/Chicago), and sporadically present in the channel at other times. +Informal discussion regarding bugs, new features, and implementation of existing features takes place in the `#internals` channel of the [LaraChat](https://larachat.co) Slack team. Taylor Otwell, the maintainer of Laravel, is typically present in the channel on weekdays from 8am-5pm (UTC-06:00 or America/Chicago), and sporadically present in the channel at other times. ## Which Branch? @@ -46,7 +49,7 @@ Informal discussion regarding bugs, new features, and implementation of existing **Major** new features should always be sent to the `master` branch, which contains the upcoming Laravel release. -If you are unsure if your feature qualifies as a major or minor, please ask Taylor Otwell in the `#internals` channel of the [LaraChat](http://larachat.co) Slack team. +If you are unsure if your feature qualifies as a major or minor, please ask Taylor Otwell in the `#internals` channel of the [LaraChat](https://larachat.co) Slack team. ## Security Vulnerabilities diff --git a/controllers.md b/controllers.md index 402f2fb..307df0d 100644 --- a/controllers.md +++ b/controllers.md @@ -107,7 +107,7 @@ However, it is more convenient to specify middleware within your controller's co class UserController extends Controller { /** - * Instantiate a new new controller instance. + * Instantiate a new controller instance. * * @return void */ @@ -121,6 +121,14 @@ However, it is more convenient to specify middleware within your controller's co } } +Controllers also allow you to register middleware using a Closure. This provides a convenient way to define a middleware for a single controller without defining an entire middleware class: + + $this->middleware(function ($request, $next) { + // ... + + return $next($request); + }); + > {tip} You may assign middleware to a subset of controller actions; however, it may indicate your controller is growing too large. Instead, consider breaking your controller into multiple, smaller controllers. @@ -150,6 +158,12 @@ GET | `/photos/{photo}/edit` | edit | photos.edit PUT/PATCH | `/photos/{photo}` | update | photos.update DELETE | `/photos/{photo}` | destroy | photos.destroy +#### Specifying The Resource Model + +If you are using route model binding and would like the resource controller's methods to type-hint a model instance, you may use the `--model` option when generating the controller: + + php artisan make:controller PhotoController --resource --model=Photo + #### Spoofing Form Methods Since HTML forms can't make `PUT`, `PATCH`, or `DELETE` requests, you will need to add a hidden `_method` field to spoof these HTTP verbs. The `method_field` helper can create this field for you: diff --git a/csrf.md b/csrf.md index 8f67e03..7533ffc 100644 --- a/csrf.md +++ b/csrf.md @@ -8,7 +8,7 @@ ## Introduction -Laravel makes it easy to protect your application from [cross-site request forgery](http://en.wikipedia.org/wiki/Cross-site_request_forgery) (CSRF) attacks. Cross-site request forgeries are a type of malicious exploit whereby unauthorized commands are performed on behalf of an authenticated user. +Laravel makes it easy to protect your application from [cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) (CSRF) attacks. Cross-site request forgeries are a type of malicious exploit whereby unauthorized commands are performed on behalf of an authenticated user. Laravel automatically generates a CSRF "token" for each active user session managed by the application. This token is used to verify that the authenticated user is the one actually making the requests to the application. diff --git a/database-testing.md b/database-testing.md index 27898fa..36f10e8 100644 --- a/database-testing.md +++ b/database-testing.md @@ -5,7 +5,7 @@ - [Using Migrations](#using-migrations) - [Using Transactions](#using-transactions) - [Writing Factories](#writing-factories) - - [Factory Types](#factory-types) + - [Factory States](#factory-states) - [Using Factories](#using-factories) - [Creating Models](#creating-models) - [Persisting Models](#persisting-models) @@ -14,18 +14,18 @@ ## Introduction -Laravel provides a variety of helpful tools to make it easier to test your database driven applications. First, you may use the `seeInDatabase` helper to assert that data exists in the database matching a given set of criteria. For example, if you would like to verify that there is a record in the `users` table with the `email` value of `sally@example.com`, you can do the following: +Laravel provides a variety of helpful tools to make it easier to test your database driven applications. First, you may use the `assertDatabaseHas` helper to assert that data exists in the database matching a given set of criteria. For example, if you would like to verify that there is a record in the `users` table with the `email` value of `sally@example.com`, you can do the following: public function testDatabase() { // Make call to application... - $this->seeInDatabase('users', [ + $this->assertDatabaseHas('users', [ 'email' => 'sally@example.com' ]); } -Of course, the `seeInDatabase` method and other helpers like it are for convenience. You are free to use any of PHPUnit's built-in assertion methods to supplement your tests. +Of course, the `assertDatabaseHas` method and other helpers like it are for convenience. You are free to use any of PHPUnit's built-in assertion methods to supplement your tests. ## Resetting The Database After Each Test @@ -39,6 +39,9 @@ One approach to resetting the database state is to rollback the database after e visit('/') - ->see('Laravel 5'); + $response = $this->get('/'); + + // ... } } @@ -66,6 +70,9 @@ Another approach to resetting the database state is to wrap each test case in a visit('/') - ->see('Laravel 5'); + $response = $this->get('/'); + + // ... } } -> {note} This trait will only wrap the default database connection in a transaction. If your application is using multiple database connections, you will need to manually handle the transaction logic for those connections. +> {note} By default, this trait will only wrap the default database connection in a transaction. If your application is using multiple database connections, you should define a `$connectionsToTransact` property on your test class. This property should be an array of connection names to execute the transactions on. - + ## Writing Factories -When testing, it is common to need to insert a few records into your database before executing your test. Instead of manually specifying the value of each column when you create this test data, Laravel allows you to define a default set of attributes for each of your [Eloquent models](/docs/{{version}}/eloquent) using model factories. To get started, take a look at the `database/factories/ModelFactory.php` file in your application. Out of the box, this file contains one factory definition: +When testing, you may need to insert a few records into your database before executing your test. Instead of manually specifying the value of each column when you create this test data, Laravel allows you to define a default set of attributes for each of your [Eloquent models](/docs/{{version}}/eloquent) using model factories. To get started, take a look at the `database/factories/ModelFactory.php` file in your application. Out of the box, this file contains one factory definition: $factory->define(App\User::class, function (Faker\Generator $faker) { + static $password; + return [ 'name' => $faker->name, - 'email' => $faker->email, - 'password' => bcrypt(str_random(10)), + 'email' => $faker->unique()->safeEmail, + 'password' => $password ?: $password = bcrypt('secret'), 'remember_token' => str_random(10), ]; }); @@ -106,29 +116,17 @@ Within the Closure, which serves as the factory definition, you may return the d Of course, you are free to add your own additional factories to the `ModelFactory.php` file. You may also create additional factory files for each model for better organization. For example, you could create `UserFactory.php` and `CommentFactory.php` files within your `database/factories` directory. All of the files within the `factories` directory will automatically be loaded by Laravel. - -### Factory Types + +### Factory States -Sometimes you may wish to have multiple factories for the same Eloquent model class. For example, perhaps you would like to have a factory for "Administrator" users in addition to normal users. You may define these factories using the `defineAs` method: +States allow you to define discrete modifications that can be applied to your model factories in any combination. For example, your `User` model might have a `delinquent` state that modifies one of its default attribute values. You may define your state transformations using the `state` method: - $factory->defineAs(App\User::class, 'admin', function ($faker) { + $factory->state(App\User::class, 'delinquent', function ($faker) { return [ - 'name' => $faker->name, - 'email' => $faker->email, - 'password' => str_random(10), - 'remember_token' => str_random(10), - 'admin' => true, + 'account_status' => 'delinquent', ]; }); -Instead of duplicating all of the attributes from your base user factory, you may use the `raw` method to retrieve the base attributes. Once you have the attributes, simply supplement them with any additional values you require: - - $factory->defineAs(App\User::class, 'admin', function ($faker) use ($factory) { - $user = $factory->raw(App\User::class); - - return array_merge($user, ['admin' => true]); - }); - ## Using Factories @@ -149,11 +147,13 @@ You may also create a Collection of many models or create models of a given type // Create three App\User instances... $users = factory(App\User::class, 3)->make(); - // Create an "admin" App\User instance... - $user = factory(App\User::class, 'admin')->make(); +#### Applying States + +You may also apply any of your [states](#factory-states) to the models. If you would like to apply multiple state transformations to the models, you should specify the name of each state you would like to apply: + + $users = factory(App\User::class, 5)->states('delinquent')->make(); - // Create three "admin" App\User instances... - $users = factory(App\User::class, 'admin', 3)->make(); + $users = factory(App\User::class, 5)->states('premium', 'delinquent')->make(); #### Overriding Attributes @@ -210,7 +210,7 @@ You may also attach relationships to models using Closure attributes in your fac ]; }); -These Closures also receive the evaluated attribute array of the factory that contains them: +These Closures also receive the evaluated attribute array of the factory that defines them: $factory->define(App\Post::class, function ($faker) { return [ diff --git a/database.md b/database.md index ad14737..3090a0f 100644 --- a/database.md +++ b/database.md @@ -7,7 +7,6 @@ - [Running Raw SQL Queries](#running-queries) - [Listening For Query Events](#listening-for-query-events) - [Database Transactions](#database-transactions) -- [Using Multiple Database Connections](#accessing-connections) ## Introduction @@ -26,7 +25,7 @@ Laravel makes interacting with databases extremely simple across a variety of da The database configuration for your application is located at `config/database.php`. In this file you may define all of your database connections, as well as specify which connection should be used by default. Examples for most of the supported database systems are provided in this file. -By default, Laravel's sample [environment configuration](/docs/{{version}}/installation#environment-configuration) is ready to use with [Laravel Homestead](/docs/{{version}}/homestead), which is a convenient virtual machine for doing Laravel development on your local machine. Of course, you are free to modify this configuration as needed for your local database. +By default, Laravel's sample [environment configuration](/docs/{{version}}/configuration#environment-configuration) is ready to use with [Laravel Homestead](/docs/{{version}}/homestead), which is a convenient virtual machine for doing Laravel development on your local machine. Of course, you are free to modify this configuration as needed for your local database. #### SQLite Configuration @@ -206,6 +205,16 @@ You may use the `transaction` method on the `DB` facade to run a set of operatio DB::table('posts')->delete(); }); +#### Handling Deadlocks + +The `transaction` method accepts an optional second argument which defines the number of times a transaction should be reattempted when a deadlock occurs. Once these attempts have been exhausted, an exception will be thrown: + + DB::transaction(function () { + DB::table('users')->update(['votes' => 1]); + + DB::table('posts')->delete(); + }, 5); + #### Manually Using Transactions If you would like to begin a transaction manually and have complete control over rollbacks and commits, you may use the `beginTransaction` method on the `DB` facade: diff --git a/documentation.md b/documentation.md index 328d7ef..ab11db8 100644 --- a/documentation.md +++ b/documentation.md @@ -7,7 +7,7 @@ - [Installation](/docs/{{version}}/installation) - [Configuration](/docs/{{version}}/configuration) - [Directory Structure](/docs/{{version}}/structure) - - [Errors & Logging](/docs/{{version}}/errors) + - [Request Lifecycle](/docs/{{version}}/lifecycle) - Dev Environments - [Homestead](/docs/{{version}}/homestead) - [Valet](/docs/{{version}}/valet) @@ -23,29 +23,35 @@ - [Controllers](/docs/{{version}}/controllers) - [Requests](/docs/{{version}}/requests) - [Responses](/docs/{{version}}/responses) + - [Views](/docs/{{version}}/views) - [Session](/docs/{{version}}/session) - [Validation](/docs/{{version}}/validation) - Frontend - - [Views](/docs/{{version}}/views) - [Blade Templates](/docs/{{version}}/blade) - - [Compiling Assets](/docs/{{version}}/elixir) - [Localization](/docs/{{version}}/localization) + - [Frontend Scaffolding](/docs/{{version}}/frontend) + - [Compiling Assets](/docs/{{version}}/mix) - Security - [Authentication](/docs/{{version}}/authentication) + - [API Authentication](/docs/{{version}}/passport) - [Authorization](/docs/{{version}}/authorization) - - [Password Reset](/docs/{{version}}/passwords) - [Encryption](/docs/{{version}}/encryption) - [Hashing](/docs/{{version}}/hashing) -- API Development - - [Authentication](/docs/{{version}}/api-authentication) + - [Password Reset](/docs/{{version}}/passwords) - General Topics + - [Artisan Console](/docs/{{version}}/artisan) - [Broadcasting](/docs/{{version}}/broadcasting) - [Cache](/docs/{{version}}/cache) + - [Collections](/docs/{{version}}/collections) + - [Errors & Logging](/docs/{{version}}/errors) - [Events](/docs/{{version}}/events) - [File Storage](/docs/{{version}}/filesystem) + - [Helpers](/docs/{{version}}/helpers) - [Mail](/docs/{{version}}/mail) - [Notifications](/docs/{{version}}/notifications) + - [Packages](/docs/{{version}}/packages) - [Queues](/docs/{{version}}/queues) + - [Scheduled Tasks](/docs/{{version}}/scheduling) - Database - [Getting Started](/docs/{{version}}/database) - [Query Builder](/docs/{{version}}/queries) @@ -59,20 +65,15 @@ - [Collections](/docs/{{version}}/eloquent-collections) - [Mutators](/docs/{{version}}/eloquent-mutators) - [Serialization](/docs/{{version}}/eloquent-serialization) -- Artisan Console - - [Commands](/docs/{{version}}/artisan) - - [Task Scheduling](/docs/{{version}}/scheduling) - Testing - [Getting Started](/docs/{{version}}/testing) - - [Application Testing](/docs/{{version}}/application-testing) + - [HTTP Tests](/docs/{{version}}/http-tests) + - [Browser Tests](/docs/{{version}}/dusk) - [Database](/docs/{{version}}/database-testing) - [Mocking](/docs/{{version}}/mocking) - Official Packages - [Cashier](/docs/{{version}}/billing) - [Envoy](/docs/{{version}}/envoy) + - [Passport](/docs/{{version}}/passport) - [Scout](/docs/{{version}}/scout) - [Socialite](https://github.com/laravel/socialite) -- Appendix - - [Collections](/docs/{{version}}/collections) - - [Helpers](/docs/{{version}}/helpers) - - [Packages](/docs/{{version}}/packages) diff --git a/dusk.md b/dusk.md new file mode 100644 index 0000000..1010224 --- /dev/null +++ b/dusk.md @@ -0,0 +1,584 @@ +# Browser Tests (Laravel Dusk) + +- [Introduction](#introduction) +- [Installation](#installation) + - [Using Other Browsers](#using-other-browsers) +- [Getting Started](#getting-started) + - [Generating Tests](#generating-tests) + - [Running Tests](#running-tests) + - [Environment Handling](#environment-handling) + - [Creating Browsers](#creating-browsers) + - [Authentication](#authentication) +- [Interacting With Elements](#interacting-with-elements) + - [Clicking Links](#clicking-links) + - [Text, Values, & Attributes](#text-values-and-attributes) + - [Using Forms](#using-forms) + - [Attaching Files](#attaching-files) + - [Using The Keyboard](#using-the-keyboard) + - [Using The Mouse](#using-the-mouse) + - [Scoping Selectors](#scoping-selectors) + - [Waiting For Elements](#waiting-for-elements) +- [Available Assertions](#available-assertions) +- [Pages](#pages) + - [Generating Pages](#generating-pages) + - [Configuring Pages](#configuring-pages) + - [Navigating To Pages](#navigating-to-pages) + - [Shorthand Selectors](#shorthand-selectors) + - [Page Methods](#page-methods) + + +## Introduction + +Laravel Dusk provides an expressive, easy-to-use browser automation and testing API. By default, Dusk does not require you to install JDK or Selenium on your machine. Instead, Dusk uses a standalone [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/home) installation. However, you are free to utilize any other Selenium compatible driver you wish. + + +## Installation + +To get started, you should add the `laravel/dusk` Composer dependency to your project: + + composer require laravel/dusk + +Once Dusk is installed, you should register the `Laravel\Dusk\DuskServiceProvider` service provider. You should register the provider within the `register` method of your `AppServiceProvider` in order to limit the environments in which Dusk is available, since it exposes the ability to login as other users: + + use Laravel\Dusk\DuskServiceProvider; + + /** + * Register any application services. + * + * @return void + */ + public function register() + { + if ($this->app->environment('local', 'testing')) { + $this->app->register(DuskServiceProvider::class); + } + } + +Next, run the `dusk:install` Artisan command: + + php artisan dusk:install + +A `Browser` directory will be created within your `tests` directory and will contain an example test. Next, set the `APP_URL` environment variable in your `.env` file. This value should match the URL you use to access your application in a browser. + +To run your tests, use the `dusk` Artisan command. The `dusk` command accepts any argument that is also accepted by the `phpunit` command: + + php artisan dusk + + +### Using Other Browsers + +By default, Dusk uses Google Chrome and a standalone [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/home) installation to run your browser tests. However, you may start your own Selenium server and run your tests against any browser you wish. + +To get started, open your `tests/DuskTestCase.php` file, which is the base Dusk test case for your application. Within this file, you can remove the call to the `startChromeDriver` method. This will stop Dusk from automatically starting the ChromeDriver: + + /** + * Prepare for Dusk test execution. + * + * @beforeClass + * @return void + */ + public static function prepare() + { + // static::startChromeDriver(); + } + +Next, you may simply modify the `driver` method to connect to the URL and port of your choice. In addition, you may modify the "desired capabilities" that should be passed to the WebDriver: + + /** + * Create the RemoteWebDriver instance. + * + * @return \Facebook\WebDriver\Remote\RemoteWebDriver + */ + protected function driver() + { + return RemoteWebDriver::create( + 'http://localhost:4444', DesiredCapabilities::phantomjs() + ); + } + + +## Getting Started + + +### Generating Tests + +To generate a Dusk test, use the `dusk:make` Artisan command. The generated test will be placed in the `tests/Browser` directory: + + php artisan dusk:make LoginTest + + +### Running Tests + +To run your browser tests, use the `dusk` Artisan command: + + php artisan dusk + +The `dusk` command accepts any argument that is normally accepted by the PHPUnit test runner, allowing you to only run the tests for a given [group](https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.group), etc: + + php artisan dusk --group=foo + +#### Manually Starting ChromeDriver + +By default, Dusk will automatically attempt to start ChromeDriver. If this does not work for your particular system, you may manually start ChromeDriver before running the `dusk` command. If you choose to start ChromeDriver manually, you should comment out the following line of your `tests/DuskTestCase.php` file: + + /** + * Prepare for Dusk test execution. + * + * @beforeClass + * @return void + */ + public static function prepare() + { + // static::startChromeDriver(); + } + +In addition, if you start ChromeDriver on a port other than 9515, you should modify the `driver` method of the same class: + + /** + * Create the RemoteWebDriver instance. + * + * @return \Facebook\WebDriver\Remote\RemoteWebDriver + */ + protected function driver() + { + return RemoteWebDriver::create( + 'http://localhost:9515', DesiredCapabilities::chrome() + ); + } + + +### Environment Handling + +To force Dusk to use its own environment file when running tests, create a `.env.dusk.{environment}` file in the root of your project. For example, if you will be initiating the `dusk` command from your `local` environment, you should create a `.env.dusk.local` file. + +When running tests, Dusk will back-up your `.env` file and rename your Dusk environment to `.env`. Once the tests have completed, your `.env` file will be restored. + + +### Creating Browsers + +To get started, let's write a test that verifies we can log into our application. After generating a test, we can modify it to navigate to the login page, enter some credentials, and click the "Login" button. To create a browser instance, call the `browse` method: + + create([ + 'email' => 'taylor@laravel.com', + ]); + + $this->browse(function ($browser) use ($user) { + $browser->visit('/login') + ->type('email', $user->email) + ->type('password', 'secret') + ->press('Login') + ->assertPathIs('/home'); + }); + } + } + +As you can see in the example above, the `browse` method accepts a callback. A browser instance will automatically be passed to this callback by Dusk and is the main object used to interact with and make assertions against your application. + +> {tip} This test can be used to test the login screen generated by the `make:auth` Artisan command. + +#### Creating Multiple Browsers + +Sometimes you may need multiple browsers in order to properly carry out a test. For example, multiple browsers may be needed to test a chat screen that interacts with websockets. To create multiple browsers, simply "ask" for more than one browser in the signature of the callback given to the `browse` method: + + $this->browse(function ($first, $second) { + $first->loginAs(User::find(1)) + ->visit('/home') + ->waitForText('Message'); + + $second->loginAs(User::find(2)) + ->visit('/home') + ->waitForText('Message') + ->type('message', 'Hey Taylor') + ->press('Send'); + + $first->waitForText('Hey Taylor') + ->assertSee('Jeffrey Way'); + }); + + +### Authentication + +Often, you will be testing pages that require authentication. You can use Dusk's `loginAs` method in order to avoid interacting with the login screen during every test. The `loginAs` method accepts a user ID or user model instance: + + $this->browse(function ($first, $second) { + $first->loginAs(User::find(1)) + ->visit('/home'); + }); + + +## Interacting With Elements + + +### Clicking Links + +To click a link, you may use the `clickLink` method on the browser instance. The `clickLink` method will click the link that has the given display text: + + $browser->clickLink($linkText); + +> {note} This method interacts with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. + + +### Text, Values, & Attributes + +#### Retrieving & Setting Values + +Dusk provides several methods for interacting with the current display text, value, and attributes of elements on the page. For example, to get the "value" of an element that matches a given selector, use the `value` method: + + // Retrieve the value... + $value = $browser->value('selector'); + + // Set the value... + $browser->value('selector', 'value'); + +#### Retrieving Text + +The `text` method may be used to retrieve the display text of an element that matches the given selector: + + $text = $browser->text('selector'); + +#### Retrieving Attributes + +Finally, the `attribute` method may be used to retrieve an attribute of an element matching the given selector: + + $attribute = $browser->attribute('selector', 'value'); + + +### Using Forms + +#### Typing Values + +Dusk provides a variety of methods for interacting with forms and input elements. First, let's take a look at an example of typing text into an input field: + + $browser->type('email', 'taylor@laravel.com'); + +Note that, although the method accepts one if necessary, we are not required to pass a CSS selector into the `type` method. If a CSS selector is not provided, Dusk will search for an input field with the given `name` attribute. Finally, Dusk will attempt to find a `textarea` with the given `name` attribute. + +You may "clear" the value of an input using the `clear` method: + + $browser->clear('email'); + +#### Dropdowns + +To select a value in a dropdown selection box, you may use the `select` method. Like the `type` method, the `select` method does not require a full CSS selector. When passing a value to the `select` method, you should pass the underlying option value instead of the display text: + + $browser->select('size', 'Large'); + +#### Checkboxes + +To "check" a checkbox field, you may use the `check` method. Like many other input related methods, a full CSS selector is not required. If an exact selector match can't be found, Dusk will search for a checkbox with a matching `name` attribute: + + $browser->check('terms'); + + $browser->uncheck('terms'); + +#### Radio Buttons + +To "select" a radio button option, you may use the `radio` method. Like many other input related methods, a full CSS selector is not required. If an exact selector match can't be found, Dusk will search for a radio with matching `name` and `value` attributes: + + $browser->radio('version', 'php7'); + + +### Attaching Files + +The `attach` method may be used to attach a file to a `file` input element. Like many other input related methods, a full CSS selector is not required. If an exact selector match can't be found, Dusk will search for a file input with matching `name` attribute: + + $browser->attach('photo', __DIR__.'/photos/me.png'); + + +### Using The Keyboard + +The `keys` method allows you to provide more complex input sequences to a given element than normally allowed by the `type` method. For example, you may hold modifier keys entering values. In this example, the `shift` key will be held while `taylor` is entered into the element matching the given selector. After `taylor` is typed, `otwell` will be typed without any modifier keys: + + $browser->keys('selector', ['{shift}', 'taylor'], 'otwell'); + +You may even send a "hot key" to the primary CSS selector that contains your application: + + $browser->keys('.app', ['{command}', 'j']); + +> {tip} All modifier keys are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/facebook/php-webdriver/blob/community/lib/WebDriverKeys.php). + + +### Using The Mouse + +#### Clicking On Elements + +The `click` method may be used to "click" on an element matching the given selector: + + $browser->click('.selector'); + +#### Mouseover + +The `mouseover` method may be used when you need to move the mouse over an element matching the given selector: + + $browser->mouseover('.selector'); + +#### Drag & Drop + +The `drag` method may be used to drag an element matching the given selector to another element: + + $browser->drag('.from-selector', '.to-selector'); + + +### Scoping Selectors + +Sometimes you may wish to perform several operations while scoping all of the operations within a given selector. For example, you may wish to assert that some text exists only within a table and then click a button within that table. You may use the `with` method to accomplish this. All operations performed within the callback given to the `with` method will be scoped to the original selector: + + $browser->with('.table', function ($table) { + $table->assertSee('Hello World') + ->clickLink('Delete'); + }); + + +### Waiting For Elements + +When testing applications that use JavaScript extensively, it often becomes necessary to "wait" for certain elements or data to be available before proceeding with a test. Dusk makes this a cinch. Using a variety of methods, you may wait for elements to be visible on the page or even wait until a given JavaScript expression evaluates to `true`. + +#### Waiting + +If you need to pause the test for a given number of milliseconds, use the `pause` method: + + $browser->pause(1000); + +#### Waiting For Selectors + +The `waitFor` method may be used to pause the execution of the test until the element matching the given CSS selector is displayed on the page. By default, this will pause the test for a maximum of five seconds before throwing an exception. If necessary, you may pass a custom timeout threshold as the second argument to the method: + + // Wait a maximum of five seconds for the selector... + $browser->waitFor('.selector'); + + // Wait a maximum of one second for the selector... + $browser->waitFor('.selector', 1); + +You may also wait until the given selector is missing from the page: + + $browser->waitUntilMissing('.selector'); + + $browser->waitUntilMissing('.selector', 1); + +#### Scoping Selectors When Available + +Occasionally, you may wish to wait for a given selector and then interact with the element matching the selector. For example, you may wish to wait until a modal window is available and then press the "OK" button within the modal. The `whenAvailable` method may be used in this case. All element operations performed within the given callback will be scoped to the original selector: + + $browser->whenAvailable('.modal', function ($modal) { + $modal->assertSee('Hello World') + ->press('OK'); + }); + +#### Waiting For Text + +The `waitForText` method may be used to wait until the given text is displayed on the page: + + // Wait a maximum of five seconds for the text... + $browser->waitForText('Hello World'); + + // Wait a maximum of one second for the text... + $browser->waitForText('Hello World', 1); + +#### Waiting For Links + +The `waitForLink` method may be used to wait until the given link text is displayed on the page: + + // Wait a maximum of five seconds for the link... + $browser->waitForLink('Create'); + + // Wait a maximum of one second for the link... + $browser->waitForLink('Create', 1); + +#### Waiting On JavaScript Expressions + +Sometimes you may wish to pause the execution of a test until a given JavaScript expression evaluates to `true`. You may easily accomplish this using the `waitUntil` method. When passing an expression to this method, you do not need to include the `return` keyword or an ending semi-colon: + + // Wait a maximum of five seconds for the expression to be true... + $browser->waitUntil('App.dataLoaded'); + + $browser->waitUntil('App.data.servers.length > 0'); + + // Wait a maximum of one second for the expression to be true... + $browser->waitUntil('App.data.servers.length > 0', 1); + + +## Available Assertions + +Dusk provides a variety of assertions that you may make against your application. All of the available assertions are documented in the table below: + +Assertion | Description +------------- | ------------- +`$browser->assertTitle($title)` | Assert the page title matches the given text. +`$browser->assertTitleContains($title)` | Assert the page title contains the given text. +`$browser->assertPathIs('/home')` | Assert the current path matches the given path. +`$browser->assertHasCookie($name)` | Assert the given cookie is present. +`$browser->assertCookieValue($name, $value)` | Assert a cookie has a given value. +`$browser->assertPlainCookieValue($name, $value)` | Assert an unencrypted cookie has a given value. +`$browser->assertSee($text)` | Assert the given text is present on the page. +`$browser->assertDontSee($text)` | Assert the given text is not present on the page. +`$browser->assertSeeIn($selector, $text)` | Assert the given text is present within the selector. +`$browser->assertDontSeeIn($selector, $text)` | Assert the given text is not present within the selector. +`$browser->assertSeeLink($linkText)` | Assert the given link is present on the page. +`$browser->assertDontSeeLink($linkText)` | Assert the given link is not present on the page. +`$browser->assertInputValue($field, $value)` | Assert the given input field has the given value. +`$browser->assertInputValueIsNot($field, $value)` | Assert the given input field does not have the given value. +`$browser->assertChecked($field)` | Assert the given checkbox is checked. +`$browser->assertNotChecked($field)` | Assert the given checkbox is not checked. +`$browser->assertSelected($field, $value)` | Assert the given dropdown has the given value selected. +`$browser->assertNotSelected($field, $value)` | Assert the given dropdown does not have the given value selected. +`$browser->assertValue($selector, $value)` | Assert the element matching the given selector has the given value. +`$browser->assertVisible($selector)` | Assert the element matching the given selector is visible. +`$browser->assertMissing($selector)` | Assert the element matching the given selector is not visible. + + +## Pages + +Sometimes, tests require several complicated actions to be performed in sequence. This can make your tests harder to read and understand. Pages allow you to define expressive actions that may then be performed on a given page using a single method. Pages also allow you to define short-cuts to common selectors for your application or a single page. + + +### Generating Pages + +To generate a page object, use the `dusk:page` Artisan command. All page objects will be placed in the `tests/Browser/Pages` directory: + + php artisan dusk:page Login + + +### Configuring Pages + +By default, pages have three methods: `url`, `assert`, and `selectors`. We will discuss the `url` and `assert` methods now. The `selectors` method will be [discussed in more detail below](#shorthand-selectors). + +#### The `url` Method + +The `url` method should return the path of the URL that represents the page. Dusk will use this URL when navigating to the page in the browser: + + /** + * Get the URL for the page. + * + * @return string + */ + public function url() + { + return '/login'; + } + +#### The `assert` Method + +The `assert` method may make any assertions necessary to verify that the browser is actually on the given page. Completing this method is not necessary; however, you are free to make these assertions if you wish. These assertions will be run automatically when navigating to the page: + + /** + * Assert that the browser is on the page. + * + * @return void + */ + public function assert(Browser $browser) + { + $browser->assertPathIs($this->url()); + } + + +### Navigating To Pages + +Once a page has been configured, you may navigate to it using the `visit` method: + + use Tests\Browser\Pages\Login; + + $browser->visit(new Login); + +Sometimes you may already be on a given page and need to "load" the page's selectors and methods into the current test context. This is common when pressing a button and being redirected to a given page without explicitly navigating to it. In this situation, you may use the `on` method to load the page: + + use Tests\Browser\Pages\CreatePlaylist; + + $browser->visit('/dashboard') + ->clickLink('Create Playlist') + ->on(new CreatePlaylist) + ->assertSee('@create'); + + +### Shorthand Selectors + +The `selectors` method of pages allows you to define quick, easy-to-remember shortcuts for any CSS selector on your page. For example, let's define a shortcut for the "email" input field of the application's login page: + + /** + * Get the element shortcuts for the page. + * + * @return array + */ + public function elements() + { + return [ + '@email' => 'input[name=email]', + ]; + } + +Now, you may use this shorthand selector anywhere you would use a full CSS selector: + + $browser->type('@email', 'taylor@laravel.com'); + +#### Global Shorthand Selectors + +After installing Dusk, a base `Page` class will be placed in your `tests/Browser/Pages` directory. This class contains a `siteElements` method which may be used to define global shorthand selectors that should be available on every page throughout your application: + + /** + * Get the global element shortcuts for the site. + * + * @return array + */ + public static function siteElements() + { + return [ + '@element' => '#selector', + ]; + } + + +### Page Methods + +In addition to the default methods defined on pages, you may define additional methods which may be used throughout your tests. For example, let's imagine we are building a music management application. A common action for one page of the application might be to create a playlist. Instead of re-writing the logic to create a playlist in each test, you may define a `createPlaylist` method on a page class: + + type('name', $name) + ->check('share') + ->press('Create Playlist'); + } + } + +Once the method has been defined, you may use it within any test that utilizes the page. The browser instance will automatically be passed to the page method: + + use Tests\Browser\Pages\Dashboard; + + $browser->visit(new Dashboard) + ->createPlaylist('My Playlist') + ->assertSee('My Playlist'); diff --git a/elixir.md b/elixir.md deleted file mode 100644 index cb80437..0000000 --- a/elixir.md +++ /dev/null @@ -1,351 +0,0 @@ -# Compiling Assets (Laravel Elixir) - -- [Introduction](#introduction) -- [Installation & Setup](#installation) -- [Running Elixir](#running-elixir) -- [Working With Stylesheets](#working-with-stylesheets) - - [Less](#less) - - [Sass](#sass) - - [Stylus](#stylus) - - [Plain CSS](#plain-css) - - [Source Maps](#css-source-maps) -- [Working With Scripts](#working-with-scripts) - - [Webpack](#webpack) - - [Rollup](#rollup) - - [Scripts](#javascript) -- [Copying Files & Directories](#copying-files-and-directories) -- [Versioning / Cache Busting](#versioning-and-cache-busting) -- [BrowserSync](#browser-sync) - - -## Introduction - -Laravel Elixir provides a clean, fluent API for defining basic [Gulp](http://gulpjs.com) tasks for your Laravel application. Elixir supports common CSS and JavaScript pre-processors like [Sass](http://sass-lang.com) and [Webpack](https://webpack.github.io/). Using method chaining, Elixir allows you to fluently define your asset pipeline. For example: - -```javascript -elixir(function(mix) { - mix.sass('app.scss') - .webpack('app.js'); -}); -``` - -If you've ever been confused and overwhelmed about getting started with Gulp and asset compilation, you will love Laravel Elixir. However, you are not required to use it while developing your application. You are free to use any asset pipeline tool you wish, or even none at all. - - -## Installation & Setup - -#### Installing Node - -Before triggering Elixir, you must first ensure that Node.js and NPM are installed on your machine. - - node -v - npm -v - -By default, Laravel Homestead includes everything you need; however, if you aren't using Vagrant, then you can easily install the latest version of Node and NPM using simple graphical installers from [their download page](http://nodejs.org/en/download/). - -#### Gulp - -Next, you'll need to pull in [Gulp](http://gulpjs.com) as a global NPM package: - - npm install --global gulp-cli - -#### Laravel Elixir - -The only remaining step is to install Laravel Elixir. Within a fresh installation of Laravel, you'll find a `package.json` file in the root of your directory structure. The default `package.json` file includes Elixir and the Webpack JavaScript module bundler. Think of this like your `composer.json` file, except it defines Node dependencies instead of PHP. You may install the dependencies it references by running: - - npm install - -If you are developing on a Windows system or you are running your VM on a Windows host system, you may need to run the `npm install` command with the `--no-bin-links` switch enabled: - - npm install --no-bin-links - - -## Running Elixir - -Elixir is built on top of [Gulp](http://gulpjs.com), so to run your Elixir tasks you only need to run the `gulp` command in your terminal. Adding the `--production` flag to the command will instruct Elixir to minify your CSS and JavaScript files: - - // Run all tasks... - gulp - - // Run all tasks and minify all CSS and JavaScript... - gulp --production - -Upon running this command, you'll see a nicely formatted table that displays a summary of the events that just took place. - -#### Watching Assets For Changes - -The `gulp watch` command will continue running in your terminal and watch your assets for any changes. Gulp will automatically recompile your assets if you modify them while the `watch` command is running: - - gulp watch - - -## Working With Stylesheets - -The `gulpfile.js` file in your project's root directory contains all of your Elixir tasks. Elixir tasks can be chained together to define exactly how your assets should be compiled. - - -### Less - -The `less` method may be used to compile [Less](http://lesscss.org/) into CSS. The `less` method assumes that your Less files are stored in `resources/assets/less`. By default, the task will place the compiled CSS for this example in `public/css/app.css`: - -```javascript -elixir(function(mix) { - mix.less('app.less'); -}); -``` - -You may also combine multiple Less files into a single CSS file. Again, the resulting CSS will be placed in `public/css/app.css`: - -```javascript -elixir(function(mix) { - mix.less([ - 'app.less', - 'controllers.less' - ]); -}); -``` - -If you wish to customize the output location of the compiled CSS, you may pass a second argument to the `less` method: - -```javascript -elixir(function(mix) { - mix.less('app.less', 'public/stylesheets'); -}); - -// Specifying a specific output filename... -elixir(function(mix) { - mix.less('app.less', 'public/stylesheets/style.css'); -}); -``` - - -### Sass - -The `sass` method allows you to compile [Sass](http://sass-lang.com/) into CSS. Assuming your Sass files are stored at `resources/assets/sass`, you may use the method like so: - -```javascript -elixir(function(mix) { - mix.sass('app.scss'); -}); -``` - -Again, like the `less` method, you may compile multiple Sass files into a single CSS file, and even customize the output directory of the resulting CSS: - -```javascript -elixir(function(mix) { - mix.sass([ - 'app.scss', - 'controllers.scss' - ], 'public/assets/css'); -}); -``` - -#### Custom Paths - -While it's recommended that you use Laravel's default asset directories, if you require a different base directory, you may begin any file path with `./`. This instructs Elixir to begin at the project root, rather than using the default base directory. - -For example, to compile a file located at `app/assets/sass/app.scss` and output the results to `public/css/app.css`, you would make the following call to the `sass` method: - -```javascript -elixir(function(mix) { - mix.sass('./app/assets/sass/app.scss'); -}); -``` - - -### Stylus - -The `stylus` method may be used to compile [Stylus](http://stylus-lang.com/) into CSS. Assuming that your Stylus files are stored in `resources/assets/stylus`, you may call the method like so: - -```javascript -elixir(function(mix) { - mix.stylus('app.styl'); -}); -``` - -> {tip} This method's signature is identical to both `mix.less()` and `mix.sass()`. - - -### Plain CSS - -If you would just like to combine some plain CSS stylesheets into a single file, you may use the `styles` method. Paths passed to this method are relative to the `resources/assets/css` directory and the resulting CSS will be placed in `public/css/all.css`: - -```javascript -elixir(function(mix) { - mix.styles([ - 'normalize.css', - 'main.css' - ]); -}); -``` - -You may also instruct Elixir to write the resulting file to a custom directory or file by passing a second argument to the `styles` method: - -```javascript -elixir(function(mix) { - mix.styles([ - 'normalize.css', - 'main.css' - ], 'public/assets/css/site.css'); -}); -``` - - -### Source Maps - -In Elixir, source maps are enabled by default and provide better debugging information to your browser's developer tools when using compiled assets. For each relevant file that is compiled, you will find a companion `*.css.map` or `*.js.map` file in the same directory. - -If you do not want source maps generated for your application, you may disable them using the `sourcemaps` configuration option: - -```javascript -elixir.config.sourcemaps = false; - -elixir(function(mix) { - mix.sass('app.scss'); -}); -``` - - -## Working With Scripts - -Elixir provides several features to help you work with your JavaScript files, such as compiling ECMAScript 2015, module bundling, minification, and simply concatenating plain JavaScript files. - -When writing ES2015 with modules, you have your choice between [Webpack](http://webpack.github.io) and [Rollup](http://rollupjs.org/). If these tools are foreign to you, don't worry, Elixir will handle all of the hard work behind the scenes. By default, the Laravel `gulpfile` uses `webpack` to compile Javascript, but you are free to use any module bundler you like. - - -### Webpack - -The `webpack` method may be used to compile and bundle [ECMAScript 2015](https://babeljs.io/docs/learn-es2015/) into plain JavaScript. This function accepts a file path relative to the `resources/assets/js` directory and generates a single bundled file in the `public/js` directory: - -```javascript -elixir(function(mix) { - mix.webpack('app.js'); -}); -``` - -To choose a different output or base directory, simply specify your desired paths with a leading `.`. Then you may specify the paths relative to the root of your application. For example, to compile `app/assets/js/app.js` to `public/dist/app.js`: - -```javascript -elixir(function(mix) { - mix.webpack( - './resources/assets/js/app.js', - './public/dist' - ); -}); -``` - -If you'd like to leverage more of Webpack's functionality, Elixir will read any `webpack.config.js` file that is in your project root and [factor its configuration](https://webpack.github.io/docs/configuration.html) into the build process. - - - -### Rollup - -Similar to Webpack, Rollup is a next-generation bundler for ES2015. This function accepts an array of files relative to the `resources/assets/js` directory, and generates a single file in the `public/js` directory: - -```javascript -elixir(function(mix) { - mix.rollup('app.js'); -}); -``` - -Like the `webpack` method, you may customize the location of the input and output files given to the `rollup` method: - - elixir(function(mix) { - mix.rollup( - './resources/assets/js/app.js', - './public/dist' - ); - }); - - -### Scripts - -If you have multiple JavaScript files that you would like to combine into a single file, you may use the `scripts` method, which provides automatic source maps, concatenation, and minification. - -The `scripts` method assumes all paths are relative to the `resources/assets/js` directory, and will place the resulting JavaScript in `public/js/all.js` by default: - -```javascript -elixir(function(mix) { - mix.scripts([ - 'order.js', - 'forum.js' - ]); -}); -``` - -If you need to concatenate multiple sets of scripts into different files, you may make multiple calls to the `scripts` method. The second argument given to the method determines the resulting file name for each concatenation: - -```javascript -elixir(function(mix) { - mix.scripts(['app.js', 'controllers.js'], 'public/js/app.js') - .scripts(['forum.js', 'threads.js'], 'public/js/forum.js'); -}); -``` - -If you need to combine all of the scripts in a given directory, you may use the `scriptsIn` method. The resulting JavaScript will be placed in `public/js/all.js`: - -```javascript -elixir(function(mix) { - mix.scriptsIn('public/js/some/directory'); -}); -``` - -> {tip} If you intend to concatenate multiple pre-minified vendor libraries, such as jQuery, instead consider using `mix.combine()`. This will combine the files, while omitting the source map and minification steps. As a result, compile times will drastically improve. - - - -## Copying Files & Directories - -The `copy` method may be used to copy files and directories to new locations. All operations are relative to the project's root directory: - -```javascript -elixir(function(mix) { - mix.copy('vendor/foo/bar.css', 'public/css/bar.css'); -}); -``` - - -## Versioning / Cache Busting - -Many developers suffix their compiled assets with a timestamp or unique token to force browsers to load the fresh assets instead of serving stale copies of the code. Elixir can handle this for you using the `version` method. - -The `version` method accepts a file name relative to the `public` directory, and will append a unique hash to the filename, allowing for cache-busting. For example, the generated file name will look something like: `all-16d570a7.css`: - -```javascript -elixir(function(mix) { - mix.version('css/all.css'); -}); -``` - -After generating the versioned file, you may use Laravel's global `elixir` helper within your [views](/docs/{{version}}/views) to load the appropriately hashed asset. The `elixir` function will automatically determine the current name of the hashed file: - - - -#### Versioning Multiple Files - -You may pass an array to the `version` method to version multiple files: - -```javascript -elixir(function(mix) { - mix.version(['css/all.css', 'js/app.js']); -}); -``` - -Once the files have been versioned, you may use the `elixir` helper function to generate links to the proper hashed files. Remember, you only need to pass the name of the un-hashed file to the `elixir` helper function. The helper will use the un-hashed name to determine the current hashed version of the file: - - - - - - -## BrowserSync - -BrowserSync automatically refreshes your web browser after you make changes to your assets. The `browserSync` method accepts a JavaScript object with a `proxy` attribute containing the local URL for your application. Then, once you run `gulp watch` you may access your web application using port 3000 (`http://project.dev:3000`) to enjoy browser syncing: - -```javascript -elixir(function(mix) { - mix.browserSync({ - proxy: 'project.dev' - }); -}); -``` diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 6fddd3a..5cceb29 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -43,7 +43,7 @@ To define an accessor, create a `getFooAttribute` method on your model where `Fo } } -As you can see, the original value of the column is passed to the accessor, allowing you to manipulate and return the value. To access the value of the mutator, you may simply access the `first_name` attribute on a model instance: +As you can see, the original value of the column is passed to the accessor, allowing you to manipulate and return the value. To access the value of the accessor, you may simply access the `first_name` attribute on a model instance: $user = App\User::find(1); diff --git a/eloquent-relationships.md b/eloquent-relationships.md index fa31cce..f60a7ba 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -12,6 +12,7 @@ - [Querying Relations](#querying-relations) - [Relationship Methods Vs. Dynamic Properties](#relationship-methods-vs-dynamic-properties) - [Querying Relationship Existence](#querying-relationship-existence) + - [Querying Relationship Absence](#querying-relationship-absence) - [Counting Related Models](#counting-related-models) - [Eager Loading](#eager-loading) - [Constraining Eager Loads](#constraining-eager-loads) @@ -38,7 +39,7 @@ Database tables are often related to one another. For example, a blog post may h ## Defining Relationships -Eloquent relationships are defined as functions on your Eloquent model classes. Since, like Eloquent models themselves, relationships also serve as powerful [query builders](/docs/{{version}}/queries), defining relationships as functions provides powerful method chaining and querying capabilities. For example, we may chain additional constraints on this `posts` relationship: +Eloquent relationships are defined as methods on your Eloquent model classes. Since, like Eloquent models themselves, relationships also serve as powerful [query builders](/docs/{{version}}/queries), defining relationships as methods provides powerful method chaining and querying capabilities. For example, we may chain additional constraints on this `posts` relationship: $user->posts()->where('active', 1)->get(); @@ -66,7 +67,7 @@ A one-to-one relationship is a very basic relation. For example, a `User` model } } -The first argument passed to the `hasOne` method is the name of the related model. Once the relationship is defined, we may retrieve the related record using Eloquent's dynamic properties. Dynamic properties allow you to access relationship functions as if they were properties defined on the model: +The first argument passed to the `hasOne` method is the name of the related model. Once the relationship is defined, we may retrieve the related record using Eloquent's dynamic properties. Dynamic properties allow you to access relationship methods as if they were properties defined on the model: $phone = User::find(1)->phone; @@ -143,7 +144,7 @@ A "one-to-many" relationship is used to define relationships where a single mode Remember, Eloquent will automatically determine the proper foreign key column on the `Comment` model. By convention, Eloquent will take the "snake case" name of the owning model and suffix it with `_id`. So, for this example, Eloquent will assume the foreign key on the `Comment` model is `post_id`. -Once the relationship has been defined, we can access the collection of comments by accessing the `comments` property. Remember, since Eloquent provides "dynamic properties", we can access relationship functions as if they were defined as properties on the model: +Once the relationship has been defined, we can access the collection of comments by accessing the `comments` property. Remember, since Eloquent provides "dynamic properties", we can access relationship methods as if they were defined as properties on the model: $comments = App\Post::find(1)->comments; @@ -304,10 +305,31 @@ You can also filter the results returned by `belongsToMany` using the `wherePivo return $this->belongsToMany('App\Role')->wherePivotIn('priority', [1, 2]); +#### Defining Custom Intermediate Table Models + +If you would like to define a custom model to represent the intermediate table of your relationship, you may call the `using` method when defining the relationship. All custom models used to represent intermediate tables of relationships must extend the `Illuminate\Database\Eloquent\Relations\Pivot` class: + + belongsToMany('App\User')->using('App\UserRole'); + } + } + ### Has Many Through -The "has-many-through" relationship provides a convenient short-cut for accessing distant relations via an intermediate relation. For example, a `Country` model might have many `Post` models through an intermediate `User` model. In this example, you could easily gather all blog posts for a given country. Let's look at the tables required to define this relationship: +The "has-many-through" relationship provides a convenient shortcut for accessing distant relations via an intermediate relation. For example, a `Country` model might have many `Post` models through an intermediate `User` model. In this example, you could easily gather all blog posts for a given country. Let's look at the tables required to define this relationship: countries id - integer @@ -452,8 +474,8 @@ By default, Laravel will use the fully qualified class name to store the type of use Illuminate\Database\Eloquent\Relations\Relation; Relation::morphMap([ - 'posts' => App\Post::class, - 'videos' => App\Video::class, + 'posts' => 'App\Post', + 'videos' => 'App\Video', ]); You may register the `morphMap` in the `boot` function of your `AppServiceProvider` or create a separate service provider if you wish. @@ -553,7 +575,7 @@ You may also retrieve the owner of a polymorphic relation from the polymorphic m ## Querying Relations -Since all types of Eloquent relationships are defined via functions, you may call those functions to obtain an instance of the relationship without actually executing the relationship queries. In addition, all types of Eloquent relationships also serve as [query builders](/docs/{{version}}/queries), allowing you to continue to chain constraints onto the relationship query before finally executing the SQL against your database. +Since all types of Eloquent relationships are defined via methods, you may call those methods to obtain an instance of the relationship without actually executing the relationship queries. In addition, all types of Eloquent relationships also serve as [query builders](/docs/{{version}}/queries), allowing you to continue to chain constraints onto the relationship query before finally executing the SQL against your database. For example, imagine a blog system in which a `User` model has many associated `Post` models: @@ -620,6 +642,19 @@ If you need even more power, you may use the `whereHas` and `orWhereHas` methods $query->where('content', 'like', 'foo%'); })->get(); + +### Querying Relationship Absence + +When accessing the records for a model, you may wish to limit your results based on the absence of a relationship. For example, imagine you want to retrieve all blog posts that **don't** have any comments. To do so, you may pass the name of the relationship to the `doesntHave` method: + + $posts = App\Post::doesntHave('comments')->get(); + +If you need even more power, you may use the `whereDoesntHave` method to put "where" conditions on your `doesntHave` queries. This method allows you to add customized constraints to a relationship constraint, such as checking the content of a comment: + + $posts = Post::whereDoesntHave('comments', function ($query) { + $query->where('content', 'like', 'foo%'); + })->get(); + ### Counting Related Models @@ -631,7 +666,7 @@ If you want to count the number of results from a relationship without actually echo $post->comments_count; } -You may add retrieve the "counts" for multiple relations as well as add constraints to the queries: +You may add the "counts" for multiple relations as well as add constraints to the queries: $posts = Post::withCount(['votes', 'comments' => function ($query) { $query->where('content', 'like', 'foo%'); @@ -830,6 +865,12 @@ If you do not want to detach existing IDs, you may use the `syncWithoutDetaching $user->roles()->syncWithoutDetaching([1, 2, 3]); +#### Toggling Associations + +The many-to-many relationship also provides a `toggle` method which "toggles" the attachment status of the given IDs. If the given ID is currently attached, it will be detached. Likewise, if it is currently detached, it will be attached: + + $user->roles()->toggle([1, 2, 3]); + #### Saving Additional Data On A Pivot Table When working with a many-to-many relationship, the `save` method accepts an array of additional intermediate table attributes as its second argument: @@ -842,7 +883,7 @@ If you need to update an existing row in your pivot table, you may use `updateEx $user = App\User::find(1); - $user->roles()->updateExistingPivot($roleId, $attributes); + $user->roles()->updateExistingPivot($roleId, $attributes); ## Touching Parent Timestamps diff --git a/eloquent.md b/eloquent.md index c92809a..ad122be 100644 --- a/eloquent.md +++ b/eloquent.md @@ -20,6 +20,7 @@ - [Global Scopes](#global-scopes) - [Local Scopes](#local-scopes) - [Events](#events) + - [Observers](#observers) ## Introduction @@ -124,6 +125,16 @@ If you need to customize the format of your timestamps, set the `$dateFormat` pr protected $dateFormat = 'U'; } +If you need to customize the names of the columns used to store the timestamps, you may set the `CREATED_AT` and `UPDATED_AT` constants in your model: + + {note} When issuing a mass update via Eloquent, the `saved` and `updated` model events will be not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update. +> {note} When issuing a mass update via Eloquent, the `saved` and `updated` model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update. ### Mass Assignment @@ -334,6 +345,10 @@ Once we have made the attributes mass assignable, we can use the `create` method $flight = App\Flight::create(['name' => 'Flight 10']); +If you already have a model instance, you may use the `fill` method to populate it with an array of attributes: + + $flight->fill(['name' => 'Flight 22']); + #### Guarding Attributes While `$fillable` serves as a "white list" of attributes that should be mass assignable, you may also choose to use `$guarded`. The `$guarded` property should contain an array of attributes that you do not want to be mass assignable. All other attributes not in the array will be mass assignable. So, `$guarded` functions like a "black list". Of course, you should use either `$fillable` or `$guarded` - not both. In the example below, all attributes **except for `price`** will be mass assignable: @@ -366,6 +381,8 @@ If you would like to make all attributes mass assignable, you may define the `$g ### Other Creation Methods +#### `firstOrCreate`/ `firstOrNew` + There are two other methods you may use to create models by mass assigning attributes: `firstOrCreate` and `firstOrNew`. The `firstOrCreate` method will attempt to locate a database record using the given column / value pairs. If the model can not be found in the database, a record will be inserted with the given attributes. The `firstOrNew` method, like `firstOrCreate` will attempt to locate a record in the database matching the given attributes. However, if a model is not found, a new model instance will be returned. Note that the model returned by `firstOrNew` has not yet been persisted to the database. You will need to call `save` manually to persist it: @@ -376,6 +393,17 @@ The `firstOrNew` method, like `firstOrCreate` will attempt to locate a record in // Retrieve the flight by the attributes, or instantiate a new instance... $flight = App\Flight::firstOrNew(['name' => 'Flight 10']); +#### `updateOrCreate` + +You may also come across situations where you want to update an existing model or create a new model if none exists. Laravel provides an `updateOrCreate` method to do this in one step. Like the `firstOrCreate` method, `updateOrCreate` persists the model, so there's no need to call `save()`: + + // If there's a flight from Oakland to San Diego, set the price to $99. + // If no matching model exists, create one. + $flight = App\Flight::updateOrCreate( + ['departure' => 'Oakland', 'destination' => 'San Diego'], + ['price' => 99] + ); + ## Deleting Models @@ -397,10 +425,12 @@ In the example above, we are retrieving the model from the database before calli #### Deleting Models By Query -Of course, you may also run a delete query on a set of models. In this example, we will delete all flights that are marked as inactive. Like mass updates, mass deletes will not fire any model events for the models that are deleted: +Of course, you may also run a delete statement on a set of models. In this example, we will delete all flights that are marked as inactive. Like mass updates, mass deletes will not fire any model events for the models that are deleted: $deletedRows = App\Flight::where('active', 0)->delete(); +> {note} When executing a mass delete statement via Eloquent, the `deleting` and `deleted` model events will not be fired for the deleted models. This is because the models are never actually retrieved when executing the delete statement. + ### Soft Deleting @@ -519,7 +549,7 @@ Writing a global scope is simple. Define a class that implements the `Illuminate */ public function apply(Builder $builder, Model $model) { - return $builder->where('age', '>', 200); + $builder->where('age', '>', 200); } } @@ -577,16 +607,12 @@ Eloquent also allows you to define global scopes using Closures, which is partic { parent::boot(); - static::addGlobalScope('age', function(Builder $builder) { + static::addGlobalScope('age', function (Builder $builder) { $builder->where('age', '>', 200); }); } } -The first argument of the `addGlobalScope()` serves as an identifier to remove the scope: - - User::withoutGlobalScope('age')->get(); - #### Removing Global Scopes If you would like to remove a global scope for a given query, you may use the `withoutGlobalScope` method. The method accepts the class name of the global scope as its only argument: @@ -621,6 +647,7 @@ Scopes should always return a query builder instance: /** * Scope a query to only include popular users. * + * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopePopular($query) @@ -631,6 +658,7 @@ Scopes should always return a query builder instance: /** * Scope a query to only include active users. * + * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) @@ -660,6 +688,8 @@ Sometimes you may wish to define a scope that accepts parameters. To get started /** * Scope a query to only include users of a given type. * + * @param \Illuminate\Database\Eloquent\Builder $query + * @param mixed $type * @return \Illuminate\Database\Eloquent\Builder */ public function scopeOfType($query, $type) @@ -675,17 +705,80 @@ Now, you may pass the parameters when calling the scope: ## Events -Eloquent models fire several events, allowing you to hook into various points in the model's lifecycle using the following methods: `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `restoring`, `restored`. Events allow you to easily execute code each time a specific model class is saved or updated in the database. +Eloquent models fire several events, allowing you to hook into the following points in a model's lifecycle: `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `restoring`, `restored`. Events allow you to easily execute code each time a specific model class is saved or updated in the database. Whenever a new model is saved for the first time, the `creating` and `created` events will fire. If a model already existed in the database and the `save` method is called, the `updating` / `updated` events will fire. However, in both cases, the `saving` / `saved` events will fire. -For example, let's define an Eloquent event listener in a [service provider](/docs/{{version}}/providers). Within our event listener, we will call the `isValid` method on the given model, and return `false` if the model is not valid. Returning `false` from an Eloquent event listener will cancel the `save` / `update` operation: +To get started, define an `$events` property on your Eloquent model that maps various points of the Eloquent model's lifecycle to your own [event classes](/docs/{{version}}/events): + + UserSaved::class, + 'deleted' => UserDeleted::class, + ]; + } + + +### Observers + +If you are listening for many events on a given model, you may use observers to group all of your listeners into a single class. Observers classes have method names which reflect the Eloquent events you wish to listen for. Each of these methods receives the model as their only argument. Laravel does not include a default directory for observers, so you may create any directory you like to house your observer classes: + + isValid(); - }); + User::observe(UserObserver::class); } /** diff --git a/encryption.md b/encryption.md index a0a9130..397777a 100644 --- a/encryption.md +++ b/encryption.md @@ -48,7 +48,15 @@ You may encrypt a value using the `encrypt` helper. All encrypted values are enc } } -> {note} Encrypted values are passed through `serialize` during encryption, which allows for encryption of objects and arrays. Thus, non-PHP clients receiving encrypted values will need to `unserialize` the data. +#### Encrypting Without Serialization + +Encrypted values are passed through `serialize` during encryption, which allows for encryption of objects and arrays. Thus, non-PHP clients receiving encrypted values will need to `unserialize` the data. If you would like to encrypt and decrypt values without serialization, you may use the `encryptString` and `decryptString` methods of the `Crypt` facade: + + use Illuminate\Support\Facades\Crypt; + + $encrypted = Crypt::encryptString('Hello world.'); + + $decrypted = Crypt::decryptString($encrypted); #### Decrypting A Value diff --git a/envoy.md b/envoy.md index 783ef30..2155e6a 100644 --- a/envoy.md +++ b/envoy.md @@ -30,7 +30,7 @@ Since global Composer libraries can sometimes cause package version conflicts, y #### Updating Envoy -You may also use Composer to keep your Envoy installation up to date. Issuing the the `composer global update` command will update all of your globally installed Composer packages: +You may also use Composer to keep your Envoy installation up to date. Issuing the `composer global update` command will update all of your globally installed Composer packages: composer global update @@ -77,7 +77,7 @@ If needed, you may pass option values into Envoy tasks using the command line: envoy run deploy --branch=master -You may use access the options in your tasks via Blade's "echo" syntax. Of course, you may also use `if` statements and loops within your tasks. For example, let's verify the presence of the `$branch` variable before executing the `git pull` command: +You may access the options in your tasks via Blade's "echo" syntax. Of course, you may also use `if` statements and loops within your tasks. For example, let's verify the presence of the `$branch` variable before executing the `git pull` command: @servers(['web' => '192.168.1.1']) @@ -167,9 +167,9 @@ If you would like to be prompted for confirmation before running a given task on Envoy also supports sending notifications to [Slack](https://slack.com) after each task is executed. The `@slack` directive accepts a Slack hook URL and a channel name. You may retrieve your webhook URL by creating an "Incoming WebHooks" integration in your Slack control panel. You should pass the entire webhook URL into the `@slack` directive: - @after + @finished @slack('webhook-url', '#bots') - @endafter + @endfinished You may provide one of the following as the channel argument: diff --git a/errors.md b/errors.md index d099173..f4bdb32 100644 --- a/errors.md +++ b/errors.md @@ -59,7 +59,7 @@ Once this option has been configured, Laravel will log all levels greater than o If you would like to have complete control over how Monolog is configured for your application, you may use the application's `configureMonologUsing` method. You should place a call to this method in your `bootstrap/app.php` file right before the `$app` variable is returned by the file: - $app->configureMonologUsing(function($monolog) { + $app->configureMonologUsing(function ($monolog) { $monolog->pushHandler(...); }); @@ -71,7 +71,7 @@ If you would like to have complete control over how Monolog is configured for yo ### The Report Method -All exceptions are handled by the `App\Exceptions\Handler` class. This class contains two methods: `report` and `render`. We'll examine each of these methods in detail. The `report` method is used to log exceptions or send them to an external service like [BugSnag](https://bugsnag.com) or [Sentry](https://github.com/getsentry/sentry-laravel). By default, the `report` method simply passes the exception to the base class where the exception is logged. However, you are free to log exceptions however you wish. +All exceptions are handled by the `App\Exceptions\Handler` class. This class contains two methods: `report` and `render`. We'll examine each of these methods in detail. The `report` method is used to log exceptions or send them to an external service like [Bugsnag](https://bugsnag.com) or [Sentry](https://github.com/getsentry/sentry-laravel). By default, the `report` method simply passes the exception to the base class where the exception is logged. However, you are free to log exceptions however you wish. For example, if you need to report different types of exceptions in different ways, you may use the PHP `instanceof` comparison operator: @@ -80,16 +80,16 @@ For example, if you need to report different types of exceptions in different wa * * This is a great spot to send exceptions to Sentry, Bugsnag, etc. * - * @param \Exception $e + * @param \Exception $exception * @return void */ - public function report(Exception $e) + public function report(Exception $exception) { - if ($e instanceof CustomException) { + if ($exception instanceof CustomException) { // } - return parent::report($e); + return parent::report($exception); } #### Ignoring Exceptions By Type @@ -118,16 +118,16 @@ The `render` method is responsible for converting a given exception into an HTTP * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request - * @param \Exception $e + * @param \Exception $exception * @return \Illuminate\Http\Response */ - public function render($request, Exception $e) + public function render($request, Exception $exception) { - if ($e instanceof CustomException) { + if ($exception instanceof CustomException) { return response()->view('errors.custom', [], 500); } - return parent::render($request, $e); + return parent::render($request, $exception); } @@ -149,7 +149,7 @@ Laravel makes it easy to display custom error pages for various HTTP status code ## Logging -Laravel provides a simple abstraction layer on top of the powerful [Monolog](http://github.com/seldaek/monolog) library. By default, Laravel is configured to create a log file for your application in the `storage/logs` directory. You may write information to the logs using the `Log` [facade](/docs/{{version}}/facades): +Laravel provides a simple abstraction layer on top of the powerful [Monolog](https://github.com/seldaek/monolog) library. By default, Laravel is configured to create a log file for your application in the `storage/logs` directory. You may write information to the logs using the `Log` [facade](/docs/{{version}}/facades): +### Manually Registering Events + +Typically, events should be registered via the `EventServiceProvider` `$listen` array; however, you may also register Closure based events manually in the `boot` method of your `EventServiceProvider`: + + /** + * Register any other events for your application. + * + * @return void + */ + public function boot() + { + parent::boot(); + + Event::listen('event.name', function ($foo, $bar) { + // + }); + } + +#### Wildcard Event Listeners + +You may even register listeners using the `*` as a wildcard parameter, allowing you to catch multiple events on the same listener. Wildcard listeners receive the event name as their first argument, and the entire event data array as their second argument: + + Event::listen('event.*', function ($eventName, array $data) { + // + }); + ## Defining Events @@ -53,10 +81,9 @@ An event class is simply a data container which holds the information related to namespace App\Events; use App\Order; - use App\Events\Event; use Illuminate\Queue\SerializesModels; - class OrderShipped extends Event + class OrderShipped { use SerializesModels; @@ -138,6 +165,34 @@ To specify that a listener should be queued, add the `ShouldQueue` interface to That's it! Now, when this listener is called for an event, it will be automatically queued by the event dispatcher using Laravel's [queue system](/docs/{{version}}/queues). If no exceptions are thrown when the listener is executed by the queue, the queued job will automatically be deleted after it has finished processing. +#### Customizing The Queue Connection & Queue Name + +If you would like to customize the queue connection and queue name used by an event listener, you may define `$connection` and `$queue` properties on your listener class: + + ### Manually Accessing The Queue @@ -163,10 +218,38 @@ If you need to manually access the listener's underlying queue job's `delete` an } } - -## Firing Events + +### Handling Failed Jobs + +Sometimes your queued event listeners may fail. If queued listener exceeds the maximum number of attempts as defined by your queue worker, the `failed` method will be called on your listener. The `failed` method receives the event instance and the exception that caused the failure: + + +## Dispatching Events -To fire an event, you may pass an instance of the event to the `event` helper. The helper will dispatch the event to all of its registered listeners. Since the `event` helper is globally available, you may call it from anywhere in your application: +To dispatch an event, you may pass an instance of the event to the `event` helper. The helper will dispatch the event to all of its registered listeners. Since the `event` helper is globally available, you may call it from anywhere in your application: {tip} When testing, it can be helpful to assert that certain events were fired without actually triggering their listeners. Laravel's [built-in testing helpers](/docs/{{version}}/mocking#mocking-events) makes it a cinch. +> {tip} When testing, it can be helpful to assert that certain events were dispatched without actually triggering their listeners. Laravel's [built-in testing helpers](/docs/{{version}}/mocking#mocking-events) makes it a cinch. ## Event Subscribers diff --git a/facades.md b/facades.md index fc9b629..d1ee28a 100644 --- a/facades.md +++ b/facades.md @@ -27,7 +27,7 @@ Throughout the Laravel documentation, many of the examples will use facades to d Facades have many benefits. They provide a terse, memorable syntax that allows you to use Laravel's features without remembering long class names that must be injected or configured manually. Furthermore, because of their unique usage of PHP's dynamic methods, they are easy to test. -However, some care must be taken when using facades. The primary danger of facades is class scope creep. Since facades are so easy to use and do not require injection, it can be easy to let your classes continue to grow and use many facades in a single class. Using dependency injection, this potential is mitigated by the visual feedback a large constructor gives you that your class is growing too large. So, when using facades, pay special attention to the size of your class so that it's scope of responsibility stays narrow. +However, some care must be taken when using facades. The primary danger of facades is class scope creep. Since facades are so easy to use and do not require injection, it can be easy to let your classes continue to grow and use many facades in a single class. Using dependency injection, this potential is mitigated by the visual feedback a large constructor gives you that your class is growing too large. So, when using facades, pay special attention to the size of your class so that its scope of responsibility stays narrow. > {tip} When building a third-party package that interacts with Laravel, it's better to inject [Laravel contracts](/docs/{{version}}/contracts) instead of using facades. Since packages are built outside of Laravel itself, you will not have access to Laravel's facade testing helpers. @@ -150,39 +150,40 @@ Below you will find every facade and its underlying class. This is a useful tool Facade | Class | Service Container Binding ------------- | ------------- | ------------- -App | [Illuminate\Foundation\Application](http://laravel.com/api/{{version}}/Illuminate/Foundation/Application.html) | `app` -Artisan | [Illuminate\Contracts\Console\Kernel](http://laravel.com/api/{{version}}/Illuminate/Contracts/Console/Kernel.html) | `artisan` -Auth | [Illuminate\Auth\AuthManager](http://laravel.com/api/{{version}}/Illuminate/Auth/AuthManager.html) | `auth` -Blade | [Illuminate\View\Compilers\BladeCompiler](http://laravel.com/api/{{version}}/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` -Bus | [Illuminate\Contracts\Bus\Dispatcher](http://laravel.com/api/{{version}}/Illuminate/Contracts/Bus/Dispatcher.html) | -Cache | [Illuminate\Cache\Repository](http://laravel.com/api/{{version}}/Illuminate/Cache/Repository.html) | `cache` -Config | [Illuminate\Config\Repository](http://laravel.com/api/{{version}}/Illuminate/Config/Repository.html) | `config` -Cookie | [Illuminate\Cookie\CookieJar](http://laravel.com/api/{{version}}/Illuminate/Cookie/CookieJar.html) | `cookie` -Crypt | [Illuminate\Encryption\Encrypter](http://laravel.com/api/{{version}}/Illuminate/Encryption/Encrypter.html) | `encrypter` -DB | [Illuminate\Database\DatabaseManager](http://laravel.com/api/{{version}}/Illuminate/Database/DatabaseManager.html) | `db` -DB (Instance) | [Illuminate\Database\Connection](http://laravel.com/api/{{version}}/Illuminate/Database/Connection.html) | -Event | [Illuminate\Events\Dispatcher](http://laravel.com/api/{{version}}/Illuminate/Events/Dispatcher.html) | `events` -File | [Illuminate\Filesystem\Filesystem](http://laravel.com/api/{{version}}/Illuminate/Filesystem/Filesystem.html) | `files` -Gate | [Illuminate\Contracts\Auth\Access\Gate](http://laravel.com/api/5.1/Illuminate/Contracts/Auth/Access/Gate.html) | -Hash | [Illuminate\Contracts\Hashing\Hasher](http://laravel.com/api/{{version}}/Illuminate/Contracts/Hashing/Hasher.html) | `hash` -Lang | [Illuminate\Translation\Translator](http://laravel.com/api/{{version}}/Illuminate/Translation/Translator.html) | `translator` -Log | [Illuminate\Log\Writer](http://laravel.com/api/{{version}}/Illuminate/Log/Writer.html) | `log` -Mail | [Illuminate\Mail\Mailer](http://laravel.com/api/{{version}}/Illuminate/Mail/Mailer.html) | `mailer` -Password | [Illuminate\Auth\Passwords\PasswordBroker](http://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password` -Queue | [Illuminate\Queue\QueueManager](http://laravel.com/api/{{version}}/Illuminate/Queue/QueueManager.html) | `queue` -Queue (Instance) | [Illuminate\Contracts\Queue\Queue](http://laravel.com/api/{{version}}/Illuminate/Contracts/Queue/Queue.html) | `queue` -Queue (Base Class) | [Illuminate\Queue\Queue](http://laravel.com/api/{{version}}/Illuminate/Queue/Queue.html) | -Redirect | [Illuminate\Routing\Redirector](http://laravel.com/api/{{version}}/Illuminate/Routing/Redirector.html) | `redirect` -Redis | [Illuminate\Redis\Database](http://laravel.com/api/{{version}}/Illuminate/Redis/Database.html) | `redis` -Request | [Illuminate\Http\Request](http://laravel.com/api/{{version}}/Illuminate/Http/Request.html) | `request` -Response | [Illuminate\Contracts\Routing\ResponseFactory](http://laravel.com/api/{{version}}/Illuminate/Contracts/Routing/ResponseFactory.html) | -Route | [Illuminate\Routing\Router](http://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) | `router` -Schema | [Illuminate\Database\Schema\Blueprint](http://laravel.com/api/{{version}}/Illuminate/Database/Schema/Blueprint.html) | -Session | [Illuminate\Session\SessionManager](http://laravel.com/api/{{version}}/Illuminate/Session/SessionManager.html) | `session` -Session (Instance) | [Illuminate\Session\Store](http://laravel.com/api/{{version}}/Illuminate/Session/Store.html) | -Storage | [Illuminate\Contracts\Filesystem\Factory](http://laravel.com/api/{{version}}/Illuminate/Contracts/Filesystem/Factory.html) | `filesystem` -URL | [Illuminate\Routing\UrlGenerator](http://laravel.com/api/{{version}}/Illuminate/Routing/UrlGenerator.html) | `url` -Validator | [Illuminate\Validation\Factory](http://laravel.com/api/{{version}}/Illuminate/Validation/Factory.html) | `validator` -Validator (Instance) | [Illuminate\Validation\Validator](http://laravel.com/api/{{version}}/Illuminate/Validation/Validator.html) | -View | [Illuminate\View\Factory](http://laravel.com/api/{{version}}/Illuminate/View/Factory.html) | `view` -View (Instance) | [Illuminate\View\View](http://laravel.com/api/{{version}}/Illuminate/View/View.html) | +App | [Illuminate\Foundation\Application](https://laravel.com/api/{{version}}/Illuminate/Foundation/Application.html) | `app` +Artisan | [Illuminate\Contracts\Console\Kernel](https://laravel.com/api/{{version}}/Illuminate/Contracts/Console/Kernel.html) | `artisan` +Auth | [Illuminate\Auth\AuthManager](https://laravel.com/api/{{version}}/Illuminate/Auth/AuthManager.html) | `auth` +Blade | [Illuminate\View\Compilers\BladeCompiler](https://laravel.com/api/{{version}}/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` +Bus | [Illuminate\Contracts\Bus\Dispatcher](https://laravel.com/api/{{version}}/Illuminate/Contracts/Bus/Dispatcher.html) |   +Cache | [Illuminate\Cache\Repository](https://laravel.com/api/{{version}}/Illuminate/Cache/Repository.html) | `cache` +Config | [Illuminate\Config\Repository](https://laravel.com/api/{{version}}/Illuminate/Config/Repository.html) | `config` +Cookie | [Illuminate\Cookie\CookieJar](https://laravel.com/api/{{version}}/Illuminate/Cookie/CookieJar.html) | `cookie` +Crypt | [Illuminate\Encryption\Encrypter](https://laravel.com/api/{{version}}/Illuminate/Encryption/Encrypter.html) | `encrypter` +DB | [Illuminate\Database\DatabaseManager](https://laravel.com/api/{{version}}/Illuminate/Database/DatabaseManager.html) | `db` +DB (Instance) | [Illuminate\Database\Connection](https://laravel.com/api/{{version}}/Illuminate/Database/Connection.html) |   +Event | [Illuminate\Events\Dispatcher](https://laravel.com/api/{{version}}/Illuminate/Events/Dispatcher.html) | `events` +File | [Illuminate\Filesystem\Filesystem](https://laravel.com/api/{{version}}/Illuminate/Filesystem/Filesystem.html) | `files` +Gate | [Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/{{version}}/Illuminate/Contracts/Auth/Access/Gate.html) |   +Hash | [Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/{{version}}/Illuminate/Contracts/Hashing/Hasher.html) | `hash` +Lang | [Illuminate\Translation\Translator](https://laravel.com/api/{{version}}/Illuminate/Translation/Translator.html) | `translator` +Log | [Illuminate\Log\Writer](https://laravel.com/api/{{version}}/Illuminate/Log/Writer.html) | `log` +Mail | [Illuminate\Mail\Mailer](https://laravel.com/api/{{version}}/Illuminate/Mail/Mailer.html) | `mailer` +Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/api/{{version}}/Illuminate/Notifications/ChannelManager.html) |   +Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/{{version}}/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` +Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/{{version}}/Illuminate/Queue/QueueManager.html) | `queue` +Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Contracts/Queue/Queue.html) | `queue` +Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/{{version}}/Illuminate/Queue/Queue.html) |   +Redirect | [Illuminate\Routing\Redirector](https://laravel.com/api/{{version}}/Illuminate/Routing/Redirector.html) | `redirect` +Redis | [Illuminate\Redis\Database](https://laravel.com/api/{{version}}/Illuminate/Redis/Database.html) | `redis` +Request | [Illuminate\Http\Request](https://laravel.com/api/{{version}}/Illuminate/Http/Request.html) | `request` +Response | [Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/{{version}}/Illuminate/Contracts/Routing/ResponseFactory.html) |   +Route | [Illuminate\Routing\Router](https://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) | `router` +Schema | [Illuminate\Database\Schema\Blueprint](https://laravel.com/api/{{version}}/Illuminate/Database/Schema/Blueprint.html) |   +Session | [Illuminate\Session\SessionManager](https://laravel.com/api/{{version}}/Illuminate/Session/SessionManager.html) | `session` +Session (Instance) | [Illuminate\Session\Store](https://laravel.com/api/{{version}}/Illuminate/Session/Store.html) |   +Storage | [Illuminate\Contracts\Filesystem\Factory](https://laravel.com/api/{{version}}/Illuminate/Contracts/Filesystem/Factory.html) | `filesystem` +URL | [Illuminate\Routing\UrlGenerator](https://laravel.com/api/{{version}}/Illuminate/Routing/UrlGenerator.html) | `url` +Validator | [Illuminate\Validation\Factory](https://laravel.com/api/{{version}}/Illuminate/Validation/Factory.html) | `validator` +Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/api/{{version}}/Illuminate/Validation/Validator.html) |   +View | [Illuminate\View\Factory](https://laravel.com/api/{{version}}/Illuminate/View/Factory.html) | `view` +View (Instance) | [Illuminate\View\View](https://laravel.com/api/{{version}}/Illuminate/View/View.html) |   diff --git a/filesystem.md b/filesystem.md index 5d5a2f8..3de773b 100644 --- a/filesystem.md +++ b/filesystem.md @@ -4,7 +4,7 @@ - [Configuration](#configuration) - [The Public Disk](#the-public-disk) - [The Local Driver](#the-local-driver) - - [Other Driver Prerequisites](#other-driver-prerequisites) + - [Driver Prerequisites](#driver-prerequisites) - [Obtaining Disk Instances](#obtaining-disk-instances) - [Retrieving Files](#retrieving-files) - [File URLs](#file-urls) @@ -48,8 +48,8 @@ When using the `local` driver, all file operations are relative to the `root` di Storage::disk('local')->put('file.txt', 'Contents'); - -### Other Driver Prerequisites + +### Driver Prerequisites #### Composer Packages @@ -58,6 +58,10 @@ Before using the S3 or Rackspace drivers, you will need to install the appropria - Amazon S3: `league/flysystem-aws-s3-v3 ~1.0` - Rackspace: `league/flysystem-rackspace ~1.0` +#### S3 Driver Configuration + +The S3 driver configuration information is located in your `config/filesystems.php` configuration file. This file contains an example configuration array for an S3 driver. You are free to modify this array with your own S3 configuration and credentials. + #### FTP Driver Configuration Laravel's Flysystem integrations works great with FTP; however, a sample configuration is not included with the framework's default `filesystems.php` configuration file. If you need to configure a FTP filesystem, you may use the example configuration below: @@ -123,7 +127,18 @@ When using the `local` or `s3` drivers, you may use the `url` method to get the $url = Storage::url('file1.jpg'); -> {note} Remember, if you are using the `local` driver, all files that should be publicly accessible should be placed in the `storage/public` directory. Furthermore, you should [create a symbolic link](#the-public-disk) to the `storage/public` directory. +> {note} Remember, if you are using the `local` driver, all files that should be publicly accessible should be placed in the `storage/app/public` directory. Furthermore, you should [create a symbolic link](#the-public-disk) at `public/storage` which points to the `storage/app/public` directory. + +#### Local URL Host Customization + +If you would like to pre-define the host for files stored on a disk using the `local` driver, you may add a `url` option to the disk's configuration array: + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + ], ### File Metadata @@ -141,7 +156,7 @@ The `lastModified` method returns the UNIX timestamp of the last time the file w ## Storing Files -The `put` method may be used to store a file on disk. You may also pass a PHP `resource` to the `put` method, which will use Flysystem's underlying stream support. Using streams is greatly recommended when dealing with large files: +The `put` method may be used to store raw file contents on a disk. You may also pass a PHP `resource` to the `put` method, which will use Flysystem's underlying stream support. Using streams is greatly recommended when dealing with large files: use Illuminate\Support\Facades\Storage; @@ -149,6 +164,24 @@ The `put` method may be used to store a file on disk. You may also pass a PHP `r Storage::put('file.jpg', $resource); +#### Automatic Streaming + +If you would like Laravel to automatically manage streaming a given file to your storage location, you may use the `putFile` or `putFileAs` method. This method accepts either a `Illuminate\Http\File` or `Illuminate\Http\UploadedFile` instance and will automatically stream the file to your desire location: + + use Illuminate\Http\File; + + // Automatically generate a unique ID for file name... + Storage::putFile('photos', new File('/path/to/photo')); + + // Manually specify a file name... + Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg'); + +There are a few important things to note about the `putFile` method. Note that we only specified a directory name, not a file name. By default, the `putFile` method will generate a unique ID to serve as the file name. The path to the file will be returned by the `putFile` method so you can store the path, including the generated file name, in your database. + +The `putFile` and `putFileAs` methods also accept an argument to specify the "visibility" of the stored file. This is particularly useful if you are storing the file on a cloud disk such as S3 and would like the file to be publicly accessible: + + Storage::putFile('photos', new File('/path/to/photo'), 'public'); + #### Prepending & Appending To Files The `prepend` and `append` methods allow you to write to the beginning or end of a file: @@ -193,9 +226,11 @@ In web applications, one of the most common use-cases for storing files is stori } } -There are a few important things to note about this example. Note that we only specified a directory name, not a file name. By default, the `store` method will automatically generate a filename based on the contents of the file. This is accomplished by taking a MD5 hash of the file's contents. The path to the file will be returned by the `store` method so you can store the path, including the generated file name, in your database. +There are a few important things to note about this example. Note that we only specified a directory name, not a file name. By default, the `store` method will generate a unique ID to serve as the file name. The path to the file will be returned by the `store` method so you can store the path, including the generated file name, in your database. -> {note} If you are receiving very large file uploads, you may wish to manually specify the file name as shown below. Calculating an MD5 hash for extremely large files can be memory intensive. +You may also call the `putFile` method on the `Storage` facade to perform the same file manipulation as the example above: + + $path = Storage::putFile('avatars', $request->file('avatar')); #### Specifying A File Name @@ -205,6 +240,12 @@ If you would not like a file name to be automatically assigned to your stored fi 'avatars', $request->user()->id ); +Of course, you may also use the `putFileAs` method on the `Storage` facade, which will perform the same file manipulation as the example above: + + $path = Storage::putFileAs( + 'avatars', $request->file('avatar'), $request->user()->id + ); + #### Specifying A Disk By default, this method will use your default disk. If you would like to specify another disk, pass the disk name as the second argument to the `store` method: @@ -216,7 +257,7 @@ By default, this method will use your default disk. If you would like to specify ### File Visibility -In Laravel's Flysystem integration, "visibility" is an abstraction of file permissions across multiple platforms. Files may either be declared `public` or `private`. When a file is declared `public`, you are indicating that the file should generally be accessile to others. For example, when using the S3 driver, you may retrieve URLs for `public` files. +In Laravel's Flysystem integration, "visibility" is an abstraction of file permissions across multiple platforms. Files may either be declared `public` or `private`. When a file is declared `public`, you are indicating that the file should generally be accessible to others. For example, when using the S3 driver, you may retrieve URLs for `public` files. You can set the visibility when setting the file via the `put` method: @@ -301,7 +342,7 @@ In order to set up the custom filesystem you will need to create a [service prov */ public function boot() { - Storage::extend('dropbox', function($app, $config) { + Storage::extend('dropbox', function ($app, $config) { $client = new DropboxClient( $config['accessToken'], $config['clientIdentifier'] ); @@ -323,4 +364,4 @@ In order to set up the custom filesystem you will need to create a [service prov The first argument of the `extend` method is the name of the driver and the second is a Closure that receives the `$app` and `$config` variables. The resolver Closure must return an instance of `League\Flysystem\Filesystem`. The `$config` variable contains the values defined in `config/filesystems.php` for the specified disk. -Once you have created the service provider to register the extension, you may use the `dropbox` driver in your `config/filesystem.php` configuration file. +Once you have created the service provider to register the extension, you may use the `dropbox` driver in your `config/filesystems.php` configuration file. diff --git a/frontend.md b/frontend.md new file mode 100644 index 0000000..c2a779b --- /dev/null +++ b/frontend.md @@ -0,0 +1,72 @@ +# JavaScript & CSS Scaffolding + +- [Introduction](#introduction) +- [Writing CSS](#writing-css) +- [Writing JavaScript](#writing-javascript) + - [Writing Vue Components](#writing-vue-components) + + +## Introduction + +While Laravel does not dictate which JavaScript or CSS pre-processors you use, it does provide a basic starting point using [Bootstrap](https://getbootstrap.com/) and [Vue](https://vuejs.org) that will be helpful for many applications. By default, Laravel uses [NPM](https://www.npmjs.org) to install both of these frontend packages. + +#### CSS + +[Laravel Mix](/docs/{{version}}/mix) provides a clean, expressive API over compiling SASS or Less, which are extensions of plain CSS that add variables, mixins, and other powerful features that make working with CSS much more enjoyable. + +In this document, we will briefly discuss CSS compilation in general; however, you should consult the full [Laravel Mix documentation](/docs/{{version}}/mix) for more information on compiling SASS or Less. + +#### JavaScript + +Laravel does not require you to use a specific JavaScript framework or library to build your applications. In fact, you don't have to use JavaScript at all. However, Laravel does include some basic scaffolding to make it easier to get started writing modern JavaScript using the [Vue](https://vuejs.org) library. Vue provides an expressive API for building robust JavaScript applications using components. + + +## Writing CSS + +The Laravel `package.json` file includes the `bootstrap-sass` package to help you get started prototyping your application's frontend using Bootstrap. However, feel free to add or remove packages from the `package.json` file as needed for your own application. You are not required to use the Bootstrap framework to build your Laravel application - it is simply provided as a good starting point for those who choose to use it. + +Before compiling your CSS, install your project's frontend dependencies using NPM: + + npm install + +Once the dependencies have been installed using `npm install`, you can compile your SASS files to plain CSS using [Laravel Mix](/docs/{{version}}/mix#working-with-stylesheets). The `npm run dev` command will process the instructions in your `webpack.mix.js` file. Typically, your compiled CSS will be placed in the `public/css` directory: + + npm run dev + +The default `webpack.mix.js` included with Laravel will compile the `resources/assets/sass/app.scss` SASS file. This `app.scss` file imports a file of SASS variables and loads Bootstrap, which provides a good starting point for most applications. Feel free to customize the `app.scss` file however you wish or even use an entirely different pre-processor by [configuring Laravel Mix](/docs/{{version}}/mix). + + +## Writing JavaScript + +All of the JavaScript dependencies required by your application can be found in the `package.json` file in the project's root directory. This file is similar to a `composer.json` file except it specifies JavaScript dependencies instead of PHP dependencies. You can install these dependencies using the [Node package manager (NPM)](https://www.npmjs.org): + + npm install + +By default, the Laravel `package.json` file includes a few packages such as `vue` and `axios` to help you get started building your JavaScript application. Feel free to add or remove from the `package.json` file as needed for your own application. + +Once the packages are installed, you can use the `npm run dev` command to [compile your assets](/docs/{{version}}/mix). Webpack is a module bundler for modern JavaScript applications. When you run the `npm run dev` command, Webpack will execute the instructions in your `webpack.mix.js` file: + + npm run dev + +By default, the Laravel `webpack.mix.js` file compiles your SASS and the `resources/assets/js/app.js` file. Within the `app.js` file you may register your Vue components or, if you prefer a different framework, configure your own JavaScript application. Your compiled JavaScript will typically be placed in the `public/js` directory. + +> {tip} The `app.js` file will load the `resources/assets/js/bootstrap.js` file which bootstraps and configures Vue, Axios, jQuery, and all other JavaScript dependencies. If you have additional JavaScript dependencies to configure, you may do so in this file. + + +### Writing Vue Components + +By default, fresh Laravel applications contain an `Example.vue` Vue component located in the `resources/assets/js/components` directory. The `Example.vue` file is an example of a [single file Vue component](https://vuejs.org/guide/single-file-components) which defines its JavaScript and HTML template in the same file. Single file components provide a very convenient approach to building JavaScript driven applications. The example component is registered in your `app.js` file: + + Vue.component('example', require('./components/Example.vue')); + +To use the component in your application, you may simply drop it into one of your HTML templates. For example, after running the `make:auth` Artisan command to scaffold your application's authentication and registration screens, you could drop the component into the `home.blade.php` Blade template: + + @extends('layouts.app') + + @section('content') + + @endsection + +> {tip} Remember, you should run the `npm run dev` command each time you change a Vue component. Or, you may run the `npm run watch` command to monitor and automatically recompile your components each time they are modified. + +Of course, if you are interested in learning more about writing Vue components, you should read the [Vue documentation](https://vuejs.org/guide/), which provides a thorough, easy-to-read overview of the entire Vue framework. diff --git a/helpers.md b/helpers.md index 44e73e5..2cb4b52 100644 --- a/helpers.md +++ b/helpers.md @@ -39,6 +39,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [array_last](#method-array-last) [array_only](#method-array-only) [array_pluck](#method-array-pluck) +[array_prepend](#method-array-prepend) [array_pull](#method-array-pull) [array_set](#method-array-set) [array_sort](#method-array-sort) @@ -56,7 +57,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [base_path](#method-base-path) [config_path](#method-config-path) [database_path](#method-database-path) -[elixir](#method-elixir) +[mix](#method-mix) [public_path](#method-public-path) [resource_path](#method-resource-path) [storage_path](#method-storage-path) @@ -96,6 +97,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [asset](#method-asset) [secure_asset](#method-secure-asset) [route](#method-route) +[secure_url](#method-secure-url) [url](#method-url)
@@ -110,6 +112,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [auth](#method-auth) [back](#method-back) [bcrypt](#method-bcrypt) +[cache](#method-cache) [collect](#method-collect) [config](#method-config) [csrf_field](#method-csrf-field) @@ -119,11 +122,14 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [env](#method-env) [event](#method-event) [factory](#method-factory) +[info](#method-info) +[logger](#method-logger) [method_field](#method-method-field) [old](#method-old) [redirect](#method-redirect) [request](#method-request) [response](#method-response) +[retry](#method-retry) [session](#method-session) [value](#method-value) [view](#method-view) @@ -252,14 +258,18 @@ The `array_get` function also accepts a default value, which will be returned if #### `array_has()` {#collection-method} -The `array_has` function checks that a given item exists in an array using "dot" notation: +The `array_has` function checks that a given item or items exists in an array using "dot" notation: - $array = ['products' => ['desk' => ['price' => 100]]]; + $array = ['product' => ['name' => 'desk', 'price' => 100]]; - $hasDesk = array_has($array, 'products.desk'); + $hasItem = array_has($array, 'product.name'); // true + $hasItems = array_has($array, ['product.price', 'product.discount']); + + // false + #### `array_last()` {#collection-method} @@ -445,7 +455,7 @@ The `app_path` function returns the fully qualified path to the `app` directory. #### `base_path()` {#collection-method} -The `base_path` function returns the fully qualified path to the project root. You may also use the `base_path` function to generate a fully qualified path to a given file relative to the application directory: +The `base_path` function returns the fully qualified path to the project root. You may also use the `base_path` function to generate a fully qualified path to a given file relative to the project root directory: $path = base_path(); @@ -465,12 +475,12 @@ The `database_path` function returns the fully qualified path to the application $path = database_path(); - -#### `elixir()` {#collection-method} + +#### `mix()` {#collection-method} -The `elixir` function gets the path to a [versioned Elixir file](/docs/{{version}}/elixir): +The `mix` function gets the path to a [versioned Mix file](/docs/{{version}}/mix): - elixir($file); + mix($file); #### `public_path()` {#collection-method} @@ -521,7 +531,7 @@ The `class_basename` returns the class name of the given class with the class' n #### `e()` {#collection-method} -The `e` function runs `htmlentities` over the given string: +The `e` function runs `htmlspecialchars` over the given string: echo e('foo'); @@ -572,6 +582,12 @@ The `str_contains` function determines if the given string contains the given va // true +You may also pass an array of values to determine if the given string contains any of the values: + + $value = str_contains('This is my name', ['my', 'foo']); + + // true + #### `str_finish()` {#collection-method} @@ -693,14 +709,14 @@ If the method accepts route parameters, you may pass them as the second argument Generate a URL for an asset using the current scheme of the request (HTTP or HTTPS): - $url = asset('img/photo.jpg'); + $url = asset('img/photo.jpg'); #### `secure_asset()` {#collection-method} Generate a URL for an asset using HTTPS: - echo secure_asset('foo/bar.zip', $title, $attributes = []); + echo secure_asset('foo/bar.zip', $title, $attributes = []); #### `route()` {#collection-method} @@ -713,6 +729,15 @@ If the route accepts parameters, you may pass them as the second argument to the $url = route('routeName', ['id' => 1]); + +#### `secure_url()` {#collection-method} + +The `secure_url` function generates a fully qualified HTTPS URL to the given path: + + echo secure_url('user/profile'); + + echo secure_url('user/profile', [1]); + #### `url()` {#collection-method} @@ -777,6 +802,21 @@ The `bcrypt` function hashes the given value using Bcrypt. You may use it as an $password = bcrypt('my-secret-password'); + +#### `cache()` {#collection-method} + +The `cache` function may be used to get values from the cache. If the given key does not exist in the cache, an optional default value will be returned: + + $value = cache('key'); + + $value = cache('key', 'default'); + +You may add items to the cache by passing an array of key / value pairs to the function. You should also pass the number of minutes or duration the cached value should be considered valid: + + cache(['key' => 'value'], 5); + + cache(['key' => 'value'], Carbon::now()->addSeconds(10)); + #### `collect()` {#collection-method} @@ -814,10 +854,12 @@ The `csrf_token` function retrieves the value of the current CSRF token: #### `dd()` {#collection-method} -The `dd` function dumps the given variable and ends execution of the script: +The `dd` function dumps the given variables and ends execution of the script: dd($value); + dd($value1, $value2, $value3, ...); + If you do not want to halt the execution of your script, use the `dump` function instead: dump($value); @@ -853,6 +895,32 @@ The `factory` function creates a model factory builder for a given class, name, $user = factory(App\User::class)->make(); + +#### `info()` {#collection-method} + +The `info` function will write information to the log: + + info('Some helpful information!'); + +An array of contextual data may also be passed to the function: + + info('User login attempt failed.', ['id' => $user->id]); + + +#### `logger()` {#collection-method} + +The `logger` function can be used to write a `debug` level message to the log: + + logger('Debug message'); + +An array of contextual data may also be passed to the function: + + logger('User has logged in.', ['id' => $user->id]); + +A [logger](/docs/{{version}}/errors#logging) instance will be returned if no value is passed to the function: + + logger()->error('You are not allowed here.'); + #### `method_field()` {#collection-method} @@ -898,6 +966,15 @@ The `response` function creates a [response](/docs/{{version}}/responses) instan return response()->json(['foo' => 'bar'], 200, $headers); + +#### `retry()` {#collection-method} + +The `retry` function attempts to execute the given callback until the given maximum attempt threshold is met. If the callback does not throw an exception, it's return value will be returned. If the callback throws an exception, it will automatically be retried. If the maximum attempt count is exceeded, the exception will be thrown: + + return retry(5, function () { + // Attempt 5 times while resting 100ms in between attempts... + }, 100); + #### `session()` {#collection-method} @@ -920,7 +997,9 @@ The session store will be returned if no value is passed to the function: The `value` function's behavior will simply return the value it is given. However, if you pass a `Closure` to the function, the `Closure` will be executed then its result will be returned: - $value = value(function() { return 'bar'; }); + $value = value(function () { + return 'bar'; + }); #### `view()` {#collection-method} diff --git a/homestead.md b/homestead.md index dd2207f..35874b1 100644 --- a/homestead.md +++ b/homestead.md @@ -15,15 +15,17 @@ - [Configuring Cron Schedules](#configuring-cron-schedules) - [Ports](#ports) - [Network Interfaces](#network-interfaces) +- [Updating Homestead](#updating-homestead) +- [Old Versions](#old-versions) ## Introduction -Laravel strives to make the entire PHP development experience delightful, including your local development environment. [Vagrant](http://vagrantup.com) provides a simple, elegant way to manage and provision Virtual Machines. +Laravel strives to make the entire PHP development experience delightful, including your local development environment. [Vagrant](https://www.vagrantup.com) provides a simple, elegant way to manage and provision Virtual Machines. -Laravel Homestead is an official, pre-packaged Vagrant box that provides you a wonderful development environment without requiring you to install PHP, HHVM, a web server, and any other server software on your local machine. No more worrying about messing up your operating system! Vagrant boxes are completely disposable. If something goes wrong, you can destroy and re-create the box in minutes! +Laravel Homestead is an official, pre-packaged Vagrant box that provides you a wonderful development environment without requiring you to install PHP, a web server, and any other server software on your local machine. No more worrying about messing up your operating system! Vagrant boxes are completely disposable. If something goes wrong, you can destroy and re-create the box in minutes! -Homestead runs on any Windows, Mac, or Linux system, and includes the Nginx web server, PHP 7.0, MySQL, Postgres, Redis, Memcached, Node, and all of the other goodies you need to develop amazing Laravel applications. +Homestead runs on any Windows, Mac, or Linux system, and includes the Nginx web server, PHP 7.1, MySQL, Postgres, Redis, Memcached, Node, and all of the other goodies you need to develop amazing Laravel applications. > {note} If you are using Windows, you may need to enable hardware virtualization (VT-x). It can usually be enabled via your BIOS. If you are using Hyper-V on a UEFI system you may additionally need to disable Hyper-V in order to access VT-x. @@ -32,15 +34,14 @@ Homestead runs on any Windows, Mac, or Linux system, and includes the Nginx web - Ubuntu 16.04 - Git -- PHP 7.0 -- HHVM +- PHP 7.1 - Nginx - MySQL - MariaDB - Sqlite3 - Postgres - Composer -- Node (With PM2, Bower, Grunt, and Gulp) +- Node (With Yarn, PM2, Bower, Grunt, and Gulp) - Redis - Memcached - Beanstalkd @@ -51,9 +52,11 @@ Homestead runs on any Windows, Mac, or Linux system, and includes the Nginx web ### First Steps -Before launching your Homestead environment, you must install [VirtualBox 5.x](https://www.virtualbox.org/wiki/Downloads) or [VMWare](http://www.vmware.com) as well as [Vagrant](http://www.vagrantup.com/downloads.html). All of these software packages provide easy-to-use visual installers for all popular operating systems. +Before launching your Homestead environment, you must install [VirtualBox 5.1](https://www.virtualbox.org/wiki/Downloads), [VMWare](https://www.vmware.com), or [Parallels](http://www.parallels.com/products/desktop/) as well as [Vagrant](https://www.vagrantup.com/downloads.html). All of these software packages provide easy-to-use visual installers for all popular operating systems. -To use the VMware provider, you will need to purchase both VMware Fusion / Workstation and the [VMware Vagrant plug-in](http://www.vagrantup.com/vmware). Though it is not free, VMware can provide faster shared folder performance out of the box. +To use the VMware provider, you will need to purchase both VMware Fusion / Workstation and the [VMware Vagrant plug-in](https://www.vagrantup.com/vmware). Though it is not free, VMware can provide faster shared folder performance out of the box. + +To use the Parallels provider, you will need to install [Parallels Vagrant plug-in](https://github.com/Parallels/vagrant-parallels). It is free of charge. #### Installing The Homestead Vagrant Box @@ -73,14 +76,18 @@ You may install Homestead by simply cloning the repository. Consider cloning the Once you have cloned the Homestead repository, run the `bash init.sh` command from the Homestead directory to create the `Homestead.yaml` configuration file. The `Homestead.yaml` file will be placed in the `~/.homestead` hidden directory: + // Mac / Linux... bash init.sh + // Windows... + init.bat + ### Configuring Homestead #### Setting Your Provider -The `provider` key in your `~/.homestead/Homestead.yaml` file indicates which Vagrant provider should be used: `virtualbox`, `vmware_fusion`, or `vmware_workstation`. You may set this to the provider you prefer: +The `provider` key in your `~/.homestead/Homestead.yaml` file indicates which Vagrant provider should be used: `virtualbox`, `vmware_fusion`, `vmware_workstation`, or `parallels`. You may set this to the provider you prefer: provider: virtualbox @@ -92,27 +99,31 @@ The `folders` property of the `Homestead.yaml` file lists all of the folders you - map: ~/Code to: /home/vagrant/Code -To enable [NFS](http://docs.vagrantup.com/v2/synced-folders/nfs.html), just add a simple flag to your synced folder configuration: +To enable [NFS](https://www.vagrantup.com/docs/synced-folders/nfs.html), just add a simple flag to your synced folder configuration: folders: - map: ~/Code to: /home/vagrant/Code type: "nfs" -#### Configuring Nginx Sites +You may also pass any options supported by Vagrant's [Synced Folders](https://www.vagrantup.com/docs/synced-folders/basic_usage.html) by listing them under the `options` key: -Not familiar with Nginx? No problem. The `sites` property allows you to easily map a "domain" to a folder on your Homestead environment. A sample site configuration is included in the `Homestead.yaml` file. Again, you may add as many sites to your Homestead environment as necessary. Homestead can serve as a convenient, virtualized environment for every Laravel project you are working on: + folders: + - map: ~/Code + to: /home/vagrant/Code + type: "rsync" + options: + rsync__args: ["--verbose", "--archive", "--delete", "-zz"] + rsync__exclude: ["node_modules"] - sites: - - map: homestead.app - to: /home/vagrant/Code/Laravel/public -You can make any Homestead site use [HHVM](http://hhvm.com) by setting the `hhvm` option to `true`: +#### Configuring Nginx Sites + +Not familiar with Nginx? No problem. The `sites` property allows you to easily map a "domain" to a folder on your Homestead environment. A sample site configuration is included in the `Homestead.yaml` file. Again, you may add as many sites to your Homestead environment as necessary. Homestead can serve as a convenient, virtualized environment for every Laravel project you are working on: sites: - map: homestead.app to: /home/vagrant/Code/Laravel/public - hhvm: true If you change the `sites` property after provisioning the Homestead box, you should re-run `vagrant reload --provision` to update the Nginx configuration on the virtual machine. @@ -150,7 +161,7 @@ Mac / Linux: Windows: - vendor\\bin\\homestead make + vendor\\bin\\homestead make Next, run the `vagrant up` command in your terminal and access your project at `http://homestead.app` in your browser. Remember, you will still need to add an `/etc/hosts` file entry for `homestead.app` or the domain of your choice. @@ -172,7 +183,9 @@ If you prefer to use MariaDB instead of MySQL, you may add the `mariadb` option ### Accessing Homestead Globally -Sometimes you may want to `vagrant up` your Homestead machine from anywhere on your filesystem. You can do this by adding a simple Bash function to your Bash profile. This function will allow you to run any Vagrant command from anywhere on your system and will automatically point that command to your Homestead installation: +Sometimes you may want to `vagrant up` your Homestead machine from anywhere on your filesystem. You can do this on Mac / Linux systems by adding a Bash function to your Bash profile. On Windows, you may accomplish this by adding a "batch" file to your `PATH`. This scripts will allow you to run any Vagrant command from anywhere on your system and will automatically point that command to your Homestead installation: + +#### Linux function homestead() { ( cd ~/Homestead && vagrant $* ) @@ -180,6 +193,23 @@ Sometimes you may want to `vagrant up` your Homestead machine from anywhere on y Make sure to tweak the `~/Homestead` path in the function to the location of your actual Homestead installation. Once the function is installed, you may run commands like `homestead up` or `homestead ssh` from anywhere on your system. +#### Windows + +Create a `homestead.bat` batch file anywhere on your machine with the following contents: + + @echo off + + set cwd=%cd% + set homesteadVagrant=C:\Homestead + + cd /d %homesteadVagrant% && vagrant %* + cd /d %cwd% + + set cwd= + set homesteadVagrant= + +Make sure to tweak the example `C:\Homestead` path in the script to the actual location of your Homestead installation. After creating the file, add the file location to your `PATH`. You may then run commands like `homestead up` or `homestead ssh` from anywhere on your system. + ### Connecting Via SSH @@ -192,19 +222,32 @@ But, since you will probably need to SSH into your Homestead machine frequently, A `homestead` database is configured for both MySQL and Postgres out of the box. For even more convenience, Laravel's `.env` file configures the framework to use this database out of the box. -To connect to your MySQL or Postgres database from your host machine via Navicat or Sequel Pro, you should connect to `127.0.0.1` and port `33060` (MySQL) or `54320` (Postgres). The username and password for both databases is `homestead` / `secret`. +To connect to your MySQL or Postgres database from your host machine's database client, you should connect to `127.0.0.1` and port `33060` (MySQL) or `54320` (Postgres). The username and password for both databases is `homestead` / `secret`. > {note} You should only use these non-standard ports when connecting to the databases from your host machine. You will use the default 3306 and 5432 ports in your Laravel database configuration file since Laravel is running _within_ the virtual machine. ### Adding Additional Sites -Once your Homestead environment is provisioned and running, you may want to add additional Nginx sites for your Laravel applications. You can run as many Laravel installations as you wish on a single Homestead environment. To add an additional site, simply add the site to your `~/.homestead/Homestead.yaml` file and then run the `vagrant provision` terminal command from your Homestead directory. +Once your Homestead environment is provisioned and running, you may want to add additional Nginx sites for your Laravel applications. You can run as many Laravel installations as you wish on a single Homestead environment. To add an additional site, simply add the site to your `~/.homestead/Homestead.yaml` file: + + sites: + - map: homestead.app + to: /home/vagrant/Code/Laravel/public + - map: another.app + to: /home/vagrant/Code/another/public + +If Vagrant is not automatically managing your "hosts" file, you may need to add the new site to that file as well: + + 192.168.10.10 homestead.app + 192.168.10.10 another.app + +Once the site has been added, run the `vagrant reload --provision` command from your Homestead directory. ### Configuring Cron Schedules -Laravel provides a convenient way to [schedule Cron jobs](/docs/{{version}}/scheduling) by scheduling a single `schedule:run` Artisan command to be run every minute. The `schedule:run` command will examine the job scheduled defined in your `App\Console\Kernel` class to determine which jobs should be run. +Laravel provides a convenient way to [schedule Cron jobs](/docs/{{version}}/scheduling) by scheduling a single `schedule:run` Artisan command to be run every minute. The `schedule:run` command will examine the job schedule defined in your `App\Console\Kernel` class to determine which jobs should be run. If you would like the `schedule:run` command to be run for a Homestead site, you may set the `schedule` option to `true` when defining the site: @@ -258,3 +301,39 @@ To enable [DHCP](https://www.vagrantup.com/docs/networking/public_network.html), networks: - type: "public_network" bridge: "en1: Wi-Fi (AirPort)" + + +## Updating Homestead + +You can update Homestead in two simple steps. First, you should update the Vagrant box using the `vagrant box update` command: + + vagrant box update + +Next, you need to update the Homestead source code. If you cloned the repository you can simply `git pull origin master` at the location you originally cloned the repository. + +If you have installed Homestead via your project's `composer.json` file, you should ensure your `composer.json` file contains `"laravel/homestead": "^4"` and update your dependencies: + + composer update + + +## Old Versions + +You can easily override the version of the box that Homestead uses by adding the following line to your `Homestead.yaml` file: + + version: 0.6.0 + +An example: + + box: laravel/homestead + version: 0.6.0 + ip: "192.168.20.20" + memory: 2048 + cpus: 4 + provider: virtualbox + +When you use an older version of the Homestead box you need to match that with a compatible version of the Homestead source code. Below is a chart which shows the supported box versions, which version of Homestead source code to use, and the version of PHP provided: + +| | Homestead Version | Box Version | +|---|---|---| +| PHP 7.0 | 3.1.0 | 0.6.0 | +| PHP 7.1 | 4.0.0 | 1.0.0 | diff --git a/http-tests.md b/http-tests.md new file mode 100644 index 0000000..33ff0af --- /dev/null +++ b/http-tests.md @@ -0,0 +1,145 @@ +# HTTP Tests + +- [Introduction](#introduction) +- [Session / Authentication](#session-and-authentication) +- [Testing JSON APIs](#testing-json-apis) +- [Available Assertions](#available-assertions) + + +## Introduction + +Laravel provides a very fluent API for making HTTP requests to your application and examining the output. For example, take a look at the test defined below: + + get('/'); + + $response->assertStatus(200); + } + } + +The `get` method makes a `GET` request into the application, while the `assertStatus` method asserts that the returned response should have the given HTTP status code. In addition to this simple assertion, Laravel also contains a variety of assertions for inspecting the response headers, content, JSON structure, and more. + + +### Session / Authentication + +Laravel provides several helpers for working with the session during HTTP testing. First, you may set the session data to a given array using the `withSession` method. This is useful for loading the session with data before issuing a request to your application: + + withSession(['foo' => 'bar']) + ->get('/'); + } + } + +Of course, one common use of the session is for maintaining state for the authenticated user. The `actingAs` helper method provides a simple way to authenticate a given user as the current user. For example, we may use a [model factory](/docs/{{version}}/database-testing#writing-factories) to generate and authenticate a user: + + create(); + + $response = $this->actingAs($user) + ->withSession(['foo' => 'bar']) + ->get('/') + } + } + +You may also specify which guard should be used to authenticate the given user by passing the guard name as the second argument to the `actingAs` method: + + $this->actingAs($user, 'api') + + +### Testing JSON APIs + +Laravel also provides several helpers for testing JSON APIs and their responses. For example, the `json`, `get`, `post`, `put`, `patch`, and `delete` methods may be used to issue requests with various HTTP verbs. You may also easily pass data and headers to these methods. To get started, let's write a test to make a `POST` request to `/user` and assert that the expected data was returned: + + json('POST', '/user', ['name' => 'Sally']); + + $response + ->assertStatus(200) + ->assertJson([ + 'created' => true, + ]); + } + } + +> {tip} The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given fragment exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. + + +### Verifying Exact Match + +If you would like to verify that the given array is an **exact** match for the JSON returned by the application, you should use the `assertExactJson` method: + + json('POST', '/user', ['name' => 'Sally']); + + $response + ->assertStatus(200) + ->assertExactJson([ + 'created' => true, + ]); + } + } + + +### Available Assertions + +Laravel provides a variety of custom assertion methods for your [PHPUnit](https://phpunit.de/) tests. These assertions may be accessed on the response that is returned from the `json`, `get`, `post`, `put`, and `delete` test methods: + +Method | Description +------------- | ------------- +`$response->assertStatus($code);` | Assert that the response has a given code. +`$response->assertRedirect($uri);` | Assert that the response is a redirect to a given URI. +`$response->assertHeader($headerName, $value = null);` | Assert that the given header is present on the response. +`$response->assertCookie($cookieName, $value = null);` | Assert that the response contains the given cookie. +`$response->assertPlainCookie($cookieName, $value = null);` | Assert that the response contains the given cookie (unencrypted). +`$response->assertSessionHas($key, $value = null);` | Assert that the session contains the given piece of data. +`$response->assertSessionMissing($key);` | Assert that the session does not contain the given key. +`$response->assertJson(array $data);` | Assert that the response contains the given JSON data. +`$response->assertExactJson(array $data);` | Assert that the response contains an exact match of the given JSON data. +`$response->assertViewHas($key, $value = null);` | Assert that the response view was given a piece of data. diff --git a/installation.md b/installation.md index bec3277..db68b6d 100644 --- a/installation.md +++ b/installation.md @@ -4,10 +4,14 @@ - [Server Requirements](#server-requirements) - [Installing Laravel](#installing-laravel) - [Configuration](#configuration) +- [Web Server Configuration](#web-server-configuration) + - [Pretty URLs](#pretty-urls) ## Installation +> {video} Are you a visual learner? Laracasts provides a [free, thorough introduction to Laravel](https://laracasts.com/series/laravel-from-scratch-2017) for newcomers to the framework. It's a great place to start your journey. + ### Server Requirements @@ -21,12 +25,13 @@ However, if you are not using Homestead, you will need to make sure your server - PDO PHP Extension - Mbstring PHP Extension - Tokenizer PHP Extension +- XML PHP Extension ### Installing Laravel -Laravel utilizes [Composer](http://getcomposer.org) to manage its dependencies. So, before using Laravel, make sure you have Composer installed on your machine. +Laravel utilizes [Composer](https://getcomposer.org) to manage its dependencies. So, before using Laravel, make sure you have Composer installed on your machine. #### Via Laravel Installer @@ -34,7 +39,7 @@ First, download the Laravel installer using Composer: composer global require "laravel/installer" -Make sure to place the `~/.composer/vendor/bin` directory (or the equivalent directory for your OS) in your $PATH so the `laravel` executable can be located by your system. +Make sure to place the `$HOME/.composer/vendor/bin` directory (or the equivalent directory for your OS) in your $PATH so the `laravel` executable can be located by your system. Once installed, the `laravel new` command will create a fresh Laravel installation in the directory you specify. For instance, `laravel new blog` will create a directory named `blog` containing a fresh Laravel installation with all of Laravel's dependencies already installed: @@ -46,12 +51,20 @@ Alternatively, you may also install Laravel by issuing the Composer `create-proj composer create-project --prefer-dist laravel/laravel blog +#### Local Development Server + +If you have PHP installed locally and you would like to use PHP's built-in development server to serve your application, you may use the `serve` Artisan command. This command will start a development server at `http://localhost:8000`: + + php artisan serve + +Of course, more robust local development options are available via [Homestead](/docs/{{version}}/homestead) and [Valet](/docs/{{version}}/valet). + ### Configuration #### Public Directory -After installing Laravel, you should configure your your web server's document / web root to be the `public` directory. The `index.php` in this directory serves as the front controller for all HTTP requests entering your application. +After installing Laravel, you should configure your web server's document / web root to be the `public` directory. The `index.php` in this directory serves as the front controller for all HTTP requests entering your application. #### Configuration Files @@ -79,4 +92,31 @@ You may also want to configure a few additional components of Laravel, such as: - [Session](/docs/{{version}}/session#configuration) -Once Laravel is installed, you should also [configure your local environment](/docs/{{version}}/configuration#environment-configuration). + +## Web Server Configuration + + +### Pretty URLs + +#### Apache + +Laravel includes a `public/.htaccess` file that is used to provide URLs without the `index.php` front controller in the path. Before serving Laravel with Apache, be sure to enable the `mod_rewrite` module so the `.htaccess` file will be honored by the server. + +If the `.htaccess` file that ships with Laravel does not work with your Apache installation, try this alternative: + + Options +FollowSymLinks + RewriteEngine On + + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + +#### Nginx + +If you are using Nginx, the following directive in your site configuration will direct all requests to the `index.php` front controller: + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + +Of course, when using [Homestead](/docs/{{version}}/homestead) or [Valet](/docs/{{version}}/valet), pretty URLs will be automatically configured. diff --git a/lifecycle.md b/lifecycle.md index a4adf21..27e0da9 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -9,9 +9,7 @@ When using any tool in the "real world", you feel more confident if you understand how that tool works. Application development is no different. When you understand how your development tools function, you feel more comfortable and confident using them. -The goal of this document is to give you a good, high-level overview of how the Laravel framework "works". By getting to know the overall framework better, everything feels less "magical" and you will be more confident building your applications. - -If you don't understand all of the terms right away, don't lose heart! Just try to get a basic grasp of what is going on, and your knowledge will grow as you explore other sections of the documentation. +The goal of this document is to give you a good, high-level overview of how the Laravel framework works. By getting to know the overall framework better, everything feels less "magical" and you will be more confident building your applications. If you don't understand all of the terms right away, don't lose heart! Just try to get a basic grasp of what is going on, and your knowledge will grow as you explore other sections of the documentation. ## Lifecycle Overview @@ -26,9 +24,9 @@ The `index.php` file loads the Composer generated autoloader definition, and the Next, the incoming request is sent to either the HTTP kernel or the console kernel, depending on the type of request that is entering the application. These two kernels serve as the central location that all requests flow through. For now, let's just focus on the HTTP kernel, which is located in `app/Http/Kernel.php`. -The HTTP kernel extends the `Illuminate\Foundation\Http\Kernel` class, which defines an array of `bootstrappers` that will be run before the request is executed. These bootstrappers configure error handling, configure logging, [detect the application environment](/docs/{{version}}/installation#environment-configuration), and perform other tasks that need to be done before the request is actually handled. +The HTTP kernel extends the `Illuminate\Foundation\Http\Kernel` class, which defines an array of `bootstrappers` that will be run before the request is executed. These bootstrappers configure error handling, configure logging, [detect the application environment](/docs/{{version}}/configuration#environment-configuration), and perform other tasks that need to be done before the request is actually handled. -The HTTP kernel also defines a list of HTTP [middleware](/docs/{{version}}/middleware) that all requests must pass through before being handled by the application. These middleware handle reading and writing the [HTTP session](/docs/{{version}}/session), determine if the application is in maintenance mode, [verifying the CSRF token](/docs/{{version}}/routing#csrf-protection), and more. +The HTTP kernel also defines a list of HTTP [middleware](/docs/{{version}}/middleware) that all requests must pass through before being handled by the application. These middleware handle reading and writing the [HTTP session](/docs/{{version}}/session), determining if the application is in maintenance mode, [verifying the CSRF token](/docs/{{version}}/csrf), and more. The method signature for the HTTP kernel's `handle` method is quite simple: receive a `Request` and return a `Response`. Think of the Kernel as being a big black box that represents your entire application. Feed it HTTP requests and it will return HTTP responses. diff --git a/localization.md b/localization.md index e8b8ef7..075487c 100644 --- a/localization.md +++ b/localization.md @@ -1,8 +1,11 @@ # Localization - [Introduction](#introduction) -- [Retrieving Language Lines](#retrieving-language-lines) - - [Replacing Parameters In Language Lines](#replacing-parameters-in-language-lines) +- [Defining Translation Strings](#defining-translation-strings) + - [Using Short Keys](#using-short-keys) + - [Using Translation Strings As Keys](#using-translation-strings-as-keys) +- [Retrieving Translation Strings](#retrieving-translation-strings) + - [Replacing Parameters In Translation Strings](#replacing-parameters-in-translation-strings) - [Pluralization](#pluralization) - [Overriding Package Language Files](#overriding-package-language-files) @@ -36,7 +39,7 @@ The default language for your application is stored in the `config/app.php` conf // }); -You may configure a "fallback language", which will be used when the active language does not contain a given language line. Like the default language, the fallback language is also configured in the `config/app.php` configuration file: +You may configure a "fallback language", which will be used when the active language does not contain a given translation string. Like the default language, the fallback language is also configured in the `config/app.php` configuration file: 'fallback_locale' => 'en', @@ -50,31 +53,69 @@ You may use the `getLocale` and `isLocale` methods on the `App` facade to determ // } - -## Retrieving Language Lines + +## Defining Translation Strings -You may retrieve lines from language files using the `trans` helper function. The `trans` method accepts the file and key of the language line as its first argument. For example, let's retrieve the `welcome` language line from the `resources/lang/messages.php` language file: + +### Using Short Keys - echo trans('messages.welcome'); +Typically, translation strings are stored in files within the `resources/lang` directory. Within this directory there should be a subdirectory for each language supported by the application: -Of course if you are using the [Blade templating engine](/docs/{{version}}/blade), you may use the `{{ }}` syntax to echo the language line or use the `@lang` directive: + /resources + /lang + /en + messages.php + /es + messages.php + +All language files simply return an array of keyed strings. For example: + + 'Welcome to our application' + ]; + + +### Using Translation Strings As Keys + +For applications with heavy translation requirements, defining every string with a "short key" can become quickly confusing when referencing them in your views. For this reason, Laravel also provides support for defining translation strings using the "default" translation of the string as the key. + +Translation files that use translation strings as keys are stored as JSON files in the `resources/lang` directory. For example, if your application has a Spanish translation, you should create a `resources/lang/es.json` file: + + { + "I love programming.": "Me encanta la programación." + } + + +## Retrieving Translation Strings + +You may retrieve lines from language files using the `__` helper function. The `__` method accepts the file and key of the translation string as its first argument. For example, let's retrieve the `welcome` translation string from the `resources/lang/messages.php` language file: + + echo __('messages.welcome'); + + echo __('I love programming.'); + +Of course if you are using the [Blade templating engine](/docs/{{version}}/blade), you may use the `{{ }}` syntax to echo the translation string or use the `@lang` directive: + + {{ __('messages.welcome') }} @lang('messages.welcome') -If the specified language line does not exist, the `trans` function will simply return the language line key. So, using the example above, the `trans` function would return `messages.welcome` if the language line does not exist. +If the specified translation string does not exist, the `__` function will simply return the translation string key. So, using the example above, the `__` function would return `messages.welcome` if the translation string does not exist. - -### Replacing Parameters In Language Lines + +### Replacing Parameters In Translation Strings -If you wish, you may define place-holders in your language lines. All place-holders are prefixed with a `:`. For example, you may define a welcome message with a place-holder name: +If you wish, you may define place-holders in your translation strings. All place-holders are prefixed with a `:`. For example, you may define a welcome message with a place-holder name: 'welcome' => 'Welcome, :name', -To replace the place-holders when retrieving a language line, pass an array of replacements as the second argument to the `trans` function: +To replace the place-holders when retrieving a translation string, pass an array of replacements as the second argument to the `__` function: - echo trans('messages.welcome', ['name' => 'dayle']); + echo __('messages.welcome', ['name' => 'dayle']); If your place-holder contains all capital letters, or only has its first letter capitalized, the translated value will be capitalized accordingly: @@ -89,17 +130,17 @@ Pluralization is a complex problem, as different languages have a variety of com 'apples' => 'There is one apple|There are many apples', -After defining a language line that has pluralization options, you may use the `trans_choice` function to retrieve the line for a given "count". In this example, since the count is greater than one, the plural form of the language line is returned: +You may even create more complex pluralization rules which specify translation strings for multiple number ranges: - echo trans_choice('messages.apples', 10); + 'apples' => '{0} There are none|[1,19] There are some|[20,*] There are many', -Since the Laravel translator is powered by the Symfony Translation component, you may create even more complex pluralization rules which specify language lines for multiple number ranges: +After defining a translation string that has pluralization options, you may use the `trans_choice` function to retrieve the line for a given "count". In this example, since the count is greater than one, the plural form of the translation string is returned: - 'apples' => '{0} There are none|[1,19] There are some|[20,Inf] There are many', + echo trans_choice('messages.apples', 10); ## Overriding Package Language Files Some packages may ship with their own language files. Instead of changing the package's core files to tweak these lines, you may override them by placing files in the `resources/lang/vendor/{package}/{locale}` directory. -So, for example, if you need to override the English language lines in `messages.php` for a package named `skyrim/hearthfire`, you should place a language file at: `resources/lang/vendor/hearthfire/en/messages.php`. Within this file, you should only define the language lines you wish to override. Any language lines you don't override will still be loaded from the package's original language files. +So, for example, if you need to override the English translation strings in `messages.php` for a package named `skyrim/hearthfire`, you should place a language file at: `resources/lang/vendor/hearthfire/en/messages.php`. Within this file, you should only define the translation strings you wish to override. Any translation strings you don't override will still be loaded from the package's original language files. diff --git a/mail.md b/mail.md index 3e0b21b..d0968d7 100644 --- a/mail.md +++ b/mail.md @@ -9,6 +9,10 @@ - [View Data](#view-data) - [Attachments](#attachments) - [Inline Attachments](#inline-attachments) +- [Markdown Mailables](#markdown-mailables) + - [Generating Markdown Mailables](#generating-markdown-mailables) + - [Writing Markdown Messages](#writing-markdown-messages) + - [Customizing The Components](#customizing-the-components) - [Sending Mail](#sending-mail) - [Queueing Mail](#queueing-mail) - [Mail & Local Development](#mail-and-local-development) @@ -304,6 +308,91 @@ If you already have a raw data string you wish to embed into an email template, + +## Markdown Mailables + +Markdown mailable messages allow you to take advantage of the pre-built templates and components of mail notifications in your mailables. Since the messages are written in Markdown, Laravel is able to render beautiful, responsive HTML templates for the messages while also automatically generating a plain-text counterpart. + + +### Generating Markdown Mailables + +To generate a mailable with a corresponding Markdown template, you may use the `--markdown` option of the `make:mail` Artisan command: + + php artisan make:mail OrderShipped --markdown=emails.orders.shipped + +Then, when configuring the mailable within its `build` method, call the `markdown` method instead of the `view` method. The `markdown` methods accepts the name of the Markdown template and an optional array of data to make available to the template: + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + return $this->from('example@example.com') + ->markdown('emails.orders.shipped'); + } + + +### Writing Markdown Messages + +Markdown mailables use a combination of Blade components and Markdown syntax which allow you to easily construct mail messages while leveraging Laravel's pre-crafted components: + + @component('mail::message') + # Order Shipped + + Your order has been shipped! + + @component('mail::button', ['url' => $url]) + View Order + @endcomponent + + Thanks,
+ {{ config('app.name') }} + @endcomponent + +#### Button Component + +The button component renders a centered button link. The component accepts two arguments, a `url` and an optional `color`. Supported colors are `blue`, `green`, and `red`. You may add as many button components to a message as you wish: + + @component('mail::button', ['url' => $url, 'color' => 'green']) + View Order + @endcomponent + +#### Panel Component + +The panel component renders the given block of text in a panel that has a slightly different background color than the rest of the message. This allows you to draw attention to a given block of text: + + @component('mail::panel') + This is the panel content. + @endcomponent + +#### Table Component + +The table component allows you to transform a Markdown table into an HTML table. The component accepts the Markdown table as its content. Table column alignment is supported using the default Markdown table alignment syntax: + + @component('mail::table') + | Laravel | Table | Example | + | ------------- |:-------------:| --------:| + | Col 2 is | Centered | $10 | + | Col 3 is | Right-Aligned | $20 | + @endcomponent + + +### Customizing The Components + +You may export all of the Markdown mail components to your own application for customization. To export the components, use the `vendor:publish` Artisan command to publish the `laravel-mail` asset tag: + + php artisan vendor:publish --tag=laravel-mail + +This command will publish the Markdown mail components to the `resources/views/vendor/mail` directory. The `mail` directory will contain a `html` and a `markdown` directory, each containing their respective representations of every available component. You are free to customize these components however you like. + +#### Customizing The CSS + +After exporting the components, the `resources/views/vendor/mail/html/themes` directory will contain a `default.css` file. You may customize the CSS in this file and your styles will automatically be in-lined within the HTML representations of your Markdown mail messages. + +> {tip} If you would like to build an entirely new theme for the Markdown components, simply write a new CSS file within the `html/themes` directory and change the `theme` option of your `mail` configuration file. + ## Sending Mail @@ -314,6 +403,7 @@ To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/ namespace App\Http\Controllers; use App\Order; + use App\Mail\OrderShipped; use Illuminate\Http\Request; use Illuminate\Support\Facades\Mail; use App\Http\Controllers\Controller; @@ -382,6 +472,17 @@ Since all mailable classes generated using the `make:mail` command make use of t ->bcc($evenMoreUsers) ->queue($message); +#### Queueing By Default + +If you have mailable classes that you want to always be queued, you may implement the `ShouldQueue` contract on the class. Now, even if you call the `send` method when mailing, the mailable will still be queued since it implements the contract: + + use Illuminate\Contracts\Queue\ShouldQueue; + + class OrderShipped extends Mailable implements ShouldQueue + { + // + } + ## Mail & Local Development @@ -389,7 +490,7 @@ When developing an application that sends email, you probably don't want to actu #### Log Driver -Instead of sending your emails, the `log` mail driver will write all email messages to your log files for inspection. For more information on configuring your application per environment, check out the [configuration documentation](/docs/{{version}}/installation#environment-configuration). +Instead of sending your emails, the `log` mail driver will write all email messages to your log files for inspection. For more information on configuring your application per environment, check out the [configuration documentation](/docs/{{version}}/configuration#environment-configuration). #### Universal To diff --git a/middleware.md b/middleware.md index f50f33b..10f5d6a 100644 --- a/middleware.md +++ b/middleware.md @@ -36,7 +36,7 @@ This command will place a new `CheckAge` class within your `app/Http/Middleware` class CheckAge { /** - * Run the request filter. + * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next @@ -127,7 +127,7 @@ Once the middleware has been defined in the HTTP kernel, you may use the `middle // })->middleware('auth'); -Use an array to assign multiple middleware to the route: +You may also assign multiple middleware to the route: Route::get('/', function () { // diff --git a/migrations.md b/migrations.md index cc7fc2e..b01eb6f 100644 --- a/migrations.md +++ b/migrations.md @@ -51,6 +51,7 @@ Within both of these methods you may use the Laravel schema builder to expressiv create('users', function ($table) { + Schema::connection('foo')->create('users', function (Blueprint $table) { $table->increments('id'); }); You may use the `engine` property on the schema builder to define the table's storage engine: - Schema::create('users', function ($table) { + Schema::create('users', function (Blueprint $table) { $table->engine = 'InnoDB'; $table->increments('id'); @@ -193,7 +194,7 @@ Before renaming a table, you should verify that any foreign key constraints on t The `table` method on the `Schema` facade may be used to update existing tables. Like the `create` method, the `table` method accepts two arguments: the name of the table and a `Closure` that receives a `Blueprint` instance you may use to add columns to the table: - Schema::table('users', function ($table) { + Schema::table('users', function (Blueprint $table) { $table->string('email'); }); @@ -214,7 +215,7 @@ Command | Description `$table->decimal('amount', 5, 2);` | DECIMAL equivalent with a precision and scale. `$table->double('column', 15, 8);` | DOUBLE equivalent with precision, 15 digits in total and 8 after the decimal point. `$table->enum('choices', ['foo', 'bar']);` | ENUM equivalent for the database. -`$table->float('amount');` | FLOAT equivalent for the database. +`$table->float('amount', 8, 2);` | FLOAT equivalent for the database, 8 digits in total and 2 after the decimal point. `$table->increments('id');` | Incrementing ID (primary key) using a "UNSIGNED INTEGER" equivalent. `$table->integer('votes');` | INTEGER equivalent for the database. `$table->ipAddress('visitor');` | IP address equivalent for the database. @@ -222,13 +223,16 @@ Command | Description `$table->jsonb('options');` | JSONB equivalent for the database. `$table->longText('description');` | LONGTEXT equivalent for the database. `$table->macAddress('device');` | MAC address equivalent for the database. +`$table->mediumIncrements('id');` | Incrementing ID (primary key) using a "UNSIGNED MEDIUM INTEGER" equivalent. `$table->mediumInteger('numbers');` | MEDIUMINT equivalent for the database. `$table->mediumText('description');` | MEDIUMTEXT equivalent for the database. -`$table->morphs('taggable');` | Adds INTEGER `taggable_id` and STRING `taggable_type`. -`$table->nullableTimestamps();` | Same as `timestamps()`, except allows NULLs. +`$table->morphs('taggable');` | Adds unsigned INTEGER `taggable_id` and STRING `taggable_type`. +`$table->nullableMorphs('taggable');` | Nullable versions of the `morphs()` columns. +`$table->nullableTimestamps();` | Nullable versions of the `timestamps()` columns. `$table->rememberToken();` | Adds `remember_token` as VARCHAR(100) NULL. +`$table->smallIncrements('id');` | Incrementing ID (primary key) using a "UNSIGNED SMALL INTEGER" equivalent. `$table->smallInteger('votes');` | SMALLINT equivalent for the database. -`$table->softDeletes();` | Adds `deleted_at` column for soft deletes. +`$table->softDeletes();` | Adds nullable `deleted_at` column for soft deletes. `$table->string('email');` | VARCHAR equivalent column. `$table->string('name', 100);` | VARCHAR equivalent with a length. `$table->text('description');` | TEXT equivalent for the database. @@ -237,7 +241,13 @@ Command | Description `$table->tinyInteger('numbers');` | TINYINT equivalent for the database. `$table->timestamp('added_on');` | TIMESTAMP equivalent for the database. `$table->timestampTz('added_on');` | TIMESTAMP (with timezone) equivalent for the database. -`$table->timestamps();` | Adds `created_at` and `updated_at` columns. +`$table->timestamps();` | Adds nullable `created_at` and `updated_at` columns. +`$table->timestampsTz();` | Adds nullable `created_at` and `updated_at` (with timezone) columns. +`$table->unsignedBigInteger('votes');` | Unsigned BIGINT equivalent for the database. +`$table->unsignedInteger('votes');` | Unsigned INT equivalent for the database. +`$table->unsignedMediumInteger('votes');` | Unsigned MEDIUMINT equivalent for the database. +`$table->unsignedSmallInteger('votes');` | Unsigned SMALLINT equivalent for the database. +`$table->unsignedTinyInteger('votes');` | Unsigned TINYINT equivalent for the database. `$table->uuid('id');` | UUID equivalent for the database. @@ -245,7 +255,7 @@ Command | Description In addition to the column types listed above, there are several column "modifiers" you may use while adding a column to a database table. For example, to make the column "nullable", you may use the `nullable` method: - Schema::table('users', function ($table) { + Schema::table('users', function (Blueprint $table) { $table->string('email')->nullable(); }); @@ -253,12 +263,14 @@ Below is a list of all the available column modifiers. This list does not includ Modifier | Description ------------- | ------------- -`->first()` | Place the column "first" in the table (MySQL Only) `->after('column')` | Place the column "after" another column (MySQL Only) -`->nullable()` | Allow NULL values to be inserted into the column +`->comment('my comment')` | Add a comment to a column `->default($value)` | Specify a "default" value for the column +`->first()` | Place the column "first" in the table (MySQL Only) +`->nullable()` | Allow NULL values to be inserted into the column +`->storedAs($expression)` | Create a stored generated column (MySQL Only) `->unsigned()` | Set `integer` columns to `UNSIGNED` -`->comment('my comment')` | Add a comment to a column +`->virtualAs($expression)` | Create a virtual generated column (MySQL Only) @@ -272,43 +284,43 @@ Before modifying a column, be sure to add the `doctrine/dbal` dependency to your #### Updating Column Attributes -The `change` method allows you to modify an existing column to a new type or modify the column's attributes. For example, you may wish to increase the size of a string column. To see the `change` method in action, let's increase the size of the `name` column from 25 to 50: +The `change` method allows you to modify some existing column types to a new type or modify the column's attributes. For example, you may wish to increase the size of a string column. To see the `change` method in action, let's increase the size of the `name` column from 25 to 50: - Schema::table('users', function ($table) { + Schema::table('users', function (Blueprint $table) { $table->string('name', 50)->change(); }); We could also modify a column to be nullable: - Schema::table('users', function ($table) { + Schema::table('users', function (Blueprint $table) { $table->string('name', 50)->nullable()->change(); }); -> {note} Modifying any column in a table that also has a column of type `enum`, `json` or `jsonb` is not currently supported. +> {note} The following column types can not be "changed": char, double, enum, mediumInteger, timestamp, tinyInteger, ipAddress, json, jsonb, macAddress, mediumIncrements, morphs, nullableMorphs, nullableTimestamps, softDeletes, timeTz, timestampTz, timestamps, timestampsTz, unsignedMediumInteger, unsignedTinyInteger, uuid. #### Renaming Columns To rename a column, you may use the `renameColumn` method on the Schema builder. Before renaming a column, be sure to add the `doctrine/dbal` dependency to your `composer.json` file: - Schema::table('users', function ($table) { + Schema::table('users', function (Blueprint $table) { $table->renameColumn('from', 'to'); }); -> {note} Renaming any column in a table that also has a column of type `enum`, `json` or `jsonb` is not currently supported. +> {note} Renaming any column in a table that also has a column of type `enum` is not currently supported. ### Dropping Columns To drop a column, use the `dropColumn` method on the Schema builder. Before dropping columns from a SQLite database, you will need to add the `doctrine/dbal` dependency to your `composer.json` file and run the `composer update` command in your terminal to install the library: - Schema::table('users', function ($table) { + Schema::table('users', function (Blueprint $table) { $table->dropColumn('votes'); }); You may drop multiple columns from a table by passing an array of column names to the `dropColumn` method: - Schema::table('users', function ($table) { + Schema::table('users', function (Blueprint $table) { $table->dropColumn(['votes', 'avatar', 'location']); }); @@ -344,8 +356,27 @@ Command | Description `$table->primary(['first', 'last']);` | Add composite keys. `$table->unique('email');` | Add a unique index. `$table->unique('state', 'my_index_name');` | Add a custom index name. +`$table->unique(['first', 'last']);` | Add a composite unique index. `$table->index('state');` | Add a basic index. +#### Index Lengths & MySQL / MariaDB + +Laravel uses the `utf8mb4` character set by default, which includes support for storing "emojis" in the database. If you are running a version of MySQL older than the 5.7.7 release or MariaDB older than the 10.2.2 release, you may need to manually configure the default string length generated by migrations in order for MySQL to create indexes for them. You may configure this by calling the `Schema::defaultStringLength` method within your `AppServiceProvider`: + + use Illuminate\Support\Facades\Schema; + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + Schema::defaultStringLength(191); + } + +Alternatively, you may enable the `innodb_large_prefix` option for your database. Refer to your database's documentation for instructions on how to properly enable this option. + ### Dropping Indexes @@ -359,7 +390,7 @@ Command | Description If you pass an array of columns into a method that drops indexes, the conventional index name will be generated based on the table name, columns and key type: - Schema::table('geo', function ($table) { + Schema::table('geo', function (Blueprint $table) { $table->dropIndex(['state']); // Drops index 'geo_state_index' }); @@ -368,7 +399,7 @@ If you pass an array of columns into a method that drops indexes, the convention Laravel also provides support for creating foreign key constraints, which are used to force referential integrity at the database level. For example, let's define a `user_id` column on the `posts` table that references the `id` column on a `users` table: - Schema::table('posts', function ($table) { + Schema::table('posts', function (Blueprint $table) { $table->integer('user_id')->unsigned(); $table->foreign('user_id')->references('id')->on('users'); diff --git a/mix.md b/mix.md new file mode 100644 index 0000000..d05d29e --- /dev/null +++ b/mix.md @@ -0,0 +1,209 @@ +# Compiling Assets (Laravel Mix) + +- [Introduction](#introduction) +- [Installation & Setup](#installation) +- [Running Mix](#running-mix) +- [Working With Stylesheets](#working-with-stylesheets) + - [Less](#less) + - [Sass](#sass) + - [Plain CSS](#plain-css) + - [Source Maps](#css-source-maps) +- [Working With JavaScript](#working-with-scripts) + - [Code Splitting](#code-splitting) + - [Custom Webpack Configuration](#custom-webpack-configuration) +- [Copying Files & Directories](#copying-files-and-directories) +- [Versioning / Cache Busting](#versioning-and-cache-busting) +- [Notifications](#notifications) + + +## Introduction + +Laravel Mix provides a fluent API for defining Webpack build steps for your Laravel application using several common CSS and JavaScript pre-processors. Through simple method chaining, you can fluently define your asset pipeline. For example: + + mix.js('resources/assets/js/app.js', 'public/js') + .sass('resources/assets/sass/app.scss', 'public/css'); + +If you've ever been confused and overwhelmed about getting started with Webpack and asset compilation, you will love Laravel Mix. However, you are not required to use it while developing your application. Of course, you are free to use any asset pipeline tool you wish, or even none at all. + + +## Installation & Setup + +#### Installing Node + +Before triggering Mix, you must first ensure that Node.js and NPM are installed on your machine. + + node -v + npm -v + +By default, Laravel Homestead includes everything you need; however, if you aren't using Vagrant, then you can easily install the latest version of Node and NPM using simple graphical installers from [their download page](https://nodejs.org/en/download/). + +#### Laravel Mix + +The only remaining step is to install Laravel Mix. Within a fresh installation of Laravel, you'll find a `package.json` file in the root of your directory structure. The default `package.json` file includes everything you need to get started. Think of this like your `composer.json` file, except it defines Node dependencies instead of PHP. You may install the dependencies it references by running: + + npm install + +If you are developing on a Windows system or you are running your VM on a Windows host system, you may need to run the `npm install` command with the `--no-bin-links` switch enabled: + + npm install --no-bin-links + + +## Running Mix + +Mix is a configuration layer on top of [Webpack](https://webpack.js.org), so to run your Mix tasks you only need to execute one of the NPM scripts that is included with the default Laravel `package.json` file: + + // Run all Mix tasks... + npm run dev + + // Run all Mix tasks and minify output... + npm run production + +#### Watching Assets For Changes + +The `npm run watch` command will continue running in your terminal and watch all relevant files for changes. Webpack will then automatically recompile your assets when it detects a change: + + npm run watch + + +## Working With Stylesheets + +The `webpack.mix.js` file is your entry point for all asset compilation. Think of it as a light configuration wrapper around Webpack. Mix tasks can be chained together to define exactly how your assets should be compiled. + + +### Less + +The `less` method may be used to compile [Less](http://lesscss.org/) into CSS. Let's compile our primary `app.less` file to `public/css/app.css`. + + mix.less('resources/assets/less/app.less', 'public/css'); + +Multiple calls to the `less` method may be used to compile multiple files: + + mix.less('resources/assets/less/app.less', 'public/css') + .less('resources/assets/less/admin.less', 'public/css'); + +If you wish to customize the file name of the compiled CSS, you may pass a full file path as the second argument to the `less` method: + + mix.less('resources/assets/less/app.less', 'public/stylesheets/styles.css'); + + +### Sass + +The `sass` method allows you to compile [Sass](http://sass-lang.com/) into CSS. You may use the method like so: + + mix.sass('resources/assets/sass/app.scss', 'public/css'); + +Again, like the `less` method, you may compile multiple Sass files into their own respective CSS files and even customize the output directory of the resulting CSS: + + mix.sass('resources/assets/sass/app.sass', 'public/css') + .sass('resources/assets/sass/admin.sass', 'public/css/admin'); + + +### Plain CSS + +If you would just like to combine some plain CSS stylesheets into a single file, you may use the `combine` method. This method also supports concatenating JavaScript files: + + mix.combine([ + 'public/css/vendor/normalize.css', + 'public/css/vendor/videojs.css' + ], 'public/css/all.css'); + + +### Source Maps + +Though disabled by default, source maps may be activated by calling the `mix.sourceMaps()` method in your `webpack.mix.js` file. Though it comes with a compile/performance cost, this will provide extra debugging information to your browser's developer tools when using compiled assets. + + mix.js('resources/assets/js/app.js', 'public/js') + .sourceMaps(); + + +## Working With JavaScript + +Mix provides several features to help you work with your JavaScript files, such as compiling ECMAScript 2015, module bundling, minification, and simply concatenating plain JavaScript files. Even better, this all works seamlessly, without requiring an ounce of custom configuration: + + mix.js('resources/assets/js/app.js', 'public/js'); + +With this single line of code, you may now take advantage of: + +
+- ES2015 syntax. +- Compilation of `.vue` files. +- Minification for production environments. +
+ + +### Code Splitting + +One potential downside to bundling all application-specific JavaScript with your vendor libraries is that it makes long-term caching more difficult. For example, a single update to your application code will force the browser to re-download all of your vendor libraries even if they haven't changed. + +If you intend to make frequent updates to your application's JavaScript, you should consider extracting all of your vendor libraries into their file. This way, a change to your application code will not affect the caching of your large `vendor.js` file. Mix's `extract` method makes this a breeze: + + mix.js('resources/assets/js/app.js', 'public/js') + .extract(['vue']) + +The `extract` method accepts an array of all libraries or modules that you wish to extract into a `vendor.js` file. Using the above snippet as an example, Mix will generate the following files: + +
+- `public/js/manifest.js`: *The Webpack manifest runtime* +- `public/js/vendor.js`: *Your vendor libraries* +- `public/js/app.js`: *Your application code* +
+ +To avoid JavaScript errors, be sure to load these files in the proper order: + + + + + + +### Custom Webpack Configuration + +Behind the scenes, Laravel Mix references a pre-configured `webpack.config.js` file to get you up and running as quickly as possible. Occasionally, you may need to manually modify this file. You might have a special loader or plugin that needs to be referenced, or maybe you prefer to use Stylus instead of Sass. In such instances, you have two choices: + +#### Merging + +Mix provides a useful `webpackConfig` method that allows you to merge any short Webpack configuration overrides. This is a particularly appealing choice, as it doesn't require you to copy and maintain your own copy of the `webpack.config.js` file. The `webpackConfig` method accepts an object, which should contain any [Webpack-specific configuration](https://webpack.js.org/configuration/) that you wish to apply. + + mix.webpackConfig({ + resolve: { + modules: [ + path.resolve(__dirname, 'vendor/laravel/spark/resources/assets/js') + ] + } + }); + +#### Reference Your Own Configuration + +A second option is to copy Mix's `webpack.config.js` into your project root. + + cp node_modules/laravel-mix/setup/webpack.config.js ./ + +Next, you'll need to update the NPM scripts in your `package.json` to ensure that they no longer reference Mix's configuration file directly. Simply remove the `--config="node_modules/laravel-mix/setup/webpack.config.js"` entry from the commands. Once this has been done, you may freely modify your configuration file as needed. + + + +## Copying Files & Directories + +The `copy` method may be used to copy files and directories to new locations. This can be useful when a particular asset within your `node_modules` directory needs to be relocated to your `public` folder. + + mix.copy('node_modules/foo/bar.css', 'public/css/bar.css'); + + +## Versioning / Cache Busting + +Many developers suffix their compiled assets with a timestamp or unique token to force browsers to load the fresh assets instead of serving stale copies of the code. Mix can handle this for you using the `version` method. + +When executed in a production environment (`npm run production`), the `version` method will automatically append a unique hash to the filenames of all compiled files, allowing for more convenient cache busting: + + mix.js('resources/assets/js/app.js', 'public/js') + .version(); + +After generating the versioned file, you won't know the exact file name. So, you should use Laravel's global `mix` function within your [views](/docs/{{version}}/views) to load the appropriately hashed asset. The `mix` function will automatically determine the current name of the hashed file: + + + + +## Notifications + +When available, Mix will automatically display OS notifications for each bundle. This will give you instant feedback, as to whether the compilation was successful or not. However, there may be instances when you'd prefer to disable these notifications. One such example might be triggering Mix on your production server. Notifications may be deactivated, via the `disableNotifications` method. + + mix.disableNotifications(); diff --git a/mocking.md b/mocking.md index d67f802..95a7e13 100644 --- a/mocking.md +++ b/mocking.md @@ -1,47 +1,69 @@ # Mocking - [Introduction](#introduction) -- [Events](#mocking-events) -- [Jobs](#mocking-jobs) +- [Bus Fake](#bus-fake) +- [Event Fake](#event-fake) +- [Mail Fake](#mail-fake) +- [Notification Fake](#notification-fake) +- [Queue Fake](#queue-fake) - [Facades](#mocking-facades) ## Introduction -When testing Laravel applications, you may wish to "mock" certain aspects of your application so they are not actually executed during a given test. For example, when testing a controller that fires an event, you may wish to mock the event listeners so they are not actually executed during the test. This allows you to only test the controller's HTTP response without worrying about the execution of the event listeners, since the event listeners can be tested in their own test case. +When testing Laravel applications, you may wish to "mock" certain aspects of your application so they are not actually executed during a given test. For example, when testing a controller that dispatches an event, you may wish to mock the event listeners so they are not actually executed during the test. This allows you to only test the controller's HTTP response without worrying about the execution of the event listeners, since the event listeners can be tested in their own test case. -Laravel provides helpers for mocking events, jobs, and facades out of the box. These helpers primarily provide a convenience layer over Mockery so you do not have to manually make complicated Mockery method calls. Of course, are free to use [Mockery](http://docs.mockery.io/en/latest/) or PHPUnit to create your own mocks or spies. +Laravel provides helpers for mocking events, jobs, and facades out of the box. These helpers primarily provide a convenience layer over Mockery so you do not have to manually make complicated Mockery method calls. Of course, you are free to use [Mockery](http://docs.mockery.io/en/latest/) or PHPUnit to create your own mocks or spies. - -## Events + +## Bus Fake -If you are making heavy use of Laravel's event system, you may wish to silence or mock certain events while testing. For example, if you are testing user registration, you probably do not want all of a `UserRegistered` event's handlers firing, since the listeners may send "welcome" e-mails, etc. - -Laravel provides a convenient `expectsEvents` method which verifies the expected events are fired, but prevents any listeners for those events from executing: +As an alternative to mocking, you may use the `Bus` facade's `fake` method to prevent jobs from being dispatched. When using fakes, assertions are made after the code under test is executed: expectsEvents(UserRegistered::class); + Bus::fake(); - // Test user registration... + // Perform order shipping... + + Bus::assertDispatched(ShipOrder::class, function ($job) use ($order) { + return $job->order->id === $order->id; + }); + + // Assert a job was not dispatched... + Bus::assertNotDispatched(AnotherJob::class); } } -You may use the `doesntExpectEvents` method to verify that the given events are not fired: + +## Event Fake + +As an alternative to mocking, you may use the `Event` facade's `fake` method to prevent all event listeners from executing. You may then assert that events were dispatched and even inspect the data they received. When using fakes, assertions are made after the code under test is executed: expectsEvents(OrderShipped::class); - $this->doesntExpectEvents(OrderFailedToShip::class); - - // Test order shipping... - } - } + Event::fake(); -If you would like to prevent all event listeners from running, you may use the `withoutEvents` method. When this method is called, all listeners for all events will be mocked: + // Perform order shipping... - withoutEvents(); + Event::assertDispatched(OrderShipped::class, function ($e) use ($order) { + return $e->order->id === $order->id; + }); - // Test user registration code... + Event::assertNotDispatched(OrderFailedToShip::class); } } - -## Jobs - -Sometimes, you may wish to test that given jobs are dispatched when making requests to your application. This will allow you to test your routes and controllers in isolation without worrying about your job's logic. Of course, you should then test the job in a separate test case. + +## Mail Fake -Laravel provides the convenient `expectsJobs` method which will verify that the expected jobs are dispatched. However, the job itself will not be executed: +You may use the `Mail` facade's `fake` method to prevent mail from being sent. You may then assert that [mailables](/docs/{{version}}/mail) were sent to users and even inspect the data they received. When using fakes, assertions are made after the code under test is executed: expectsJobs(ShipOrder::class); + Mail::fake(); + + // Perform order shipping... - // Test order shipping... + Mail::assertSent(OrderShipped::class, function ($mail) use ($order) { + return $mail->order->id === $order->id; + }); + + // Assert a message was sent to the given users... + Mail::assertSent(OrderShipped::class, function ($mail) use ($user) { + return $mail->hasTo($user->email) && + $mail->hasCc('...') && + $mail->hasBcc('...'); + }); + + // Assert a mailable was not sent... + Mail::assertNotSent(AnotherMailable::class); } } -> {note} This method only detects jobs that are dispatched via the `DispatchesJobs` trait's dispatch methods or the `dispatch` helper function. It does not detect queued jobs that are sent directly to `Queue::push`. + +## Notification Fake -Like the event mocking helpers, you may also test that a job is not dispatched using the `doesntExpectJobs` method: +You may use the `Notification` facade's `fake` method to prevent notifications from being sent. You may then assert that [notifications](/docs/{{version}}/notifications) were sent to users and even inspect the data they received. When using fakes, assertions are made after the code under test is executed: doesntExpectJobs(ShipOrder::class); - - // Test order cancellation... + Notification::fake(); + + // Perform order shipping... + + Notification::assertSentTo( + $user, + OrderShipped::class, + function ($notification, $channels) use ($order) { + return $notification->order->id === $order->id; + } + ); + + // Assert a notification was sent to the given users... + Notification::assertSentTo( + [$user], OrderShipped::class + ); + + // Assert a notification was not sent... + Notification::assertNotSentTo( + [$user], AnotherNotification::class + ); } } -Alternatively, you may ignore all dispatched jobs using the `withoutJobs` method. When this method is called within a test method, all jobs that are dispatched during that test will be discarded: + +## Queue Fake + +As an alternative to mocking, you may use the `Queue` facade's `fake` method to prevent jobs from being queued. You may then assert that jobs were pushed to the queue and even inspect the data they received. When using fakes, assertions are made after the code under test is executed: withoutJobs(); + Queue::fake(); + + // Perform order shipping... + + Queue::assertPushed(ShipOrder::class, function ($job) use ($order) { + return $job->order->id === $order->id; + }); + + // Assert a job was pushed to a given queue... + Queue::assertPushedOn('queue-name', ShipOrder::class); - // Test order cancellation... + // Assert a job was not pushed... + Queue::assertNotPushed(AnotherJob::class); } } @@ -162,7 +234,15 @@ We can mock the call to the `Cache` facade by using the `shouldReceive` method, with('key') ->andReturn('value'); - $this->visit('/users')->see('value'); + $response = $this->get('/users'); + + // ... } } -> {note} You should not mock the `Request` facade. Instead, pass the input you desire into the HTTP helper methods such as `call` and `post` when running your test. +> {note} You should not mock the `Request` facade. Instead, pass the input you desire into the HTTP helper methods such as `get` and `post` when running your test. Likewise, instead of mocking the `Config` facade, simply call the `Config::set` method in your tests. diff --git a/notifications.md b/notifications.md index 9a4ca42..7848f04 100644 --- a/notifications.md +++ b/notifications.md @@ -11,7 +11,11 @@ - [Formatting Mail Messages](#formatting-mail-messages) - [Customizing The Recipient](#customizing-the-recipient) - [Customizing The Subject](#customizing-the-subject) - - [Error Messages](#error-messages) + - [Customizing The Templates](#customizing-the-templates) +- [Markdown Mail Notifications](#markdown-mail-notifications) + - [Generating The Message](#generating-the-message) + - [Writing The Message](#writing-the-message) + - [Customizing The Components](#customizing-the-components) - [Database Notifications](#database-notifications) - [Prerequisites](#database-prerequisites) - [Formatting Database Notifications](#formatting-database-notifications) @@ -29,10 +33,12 @@ - [Slack Notifications](#slack-notifications) - [Prerequisites](#slack-prerequisites) - [Formatting Slack Notifications](#formatting-slack-notifications) + - [Slack Attachments](#slack-attachments) - [Routing Slack Notifications](#routing-slack-notifications) - [Notification Events](#notification-events) - [Custom Channels](#custom-channels) + ## Introduction In addition to support for [sending email](/docs/{{version}}/mail), Laravel provides support for sending notifications across a variety of delivery channels, including mail, SMS (via [Nexmo](https://www.nexmo.com/)), and [Slack](https://slack.com). Notifications may also be stored in a database so they may be displayed in your web interface. @@ -54,7 +60,21 @@ This command will place a fresh notification class in your `app/Notifications` d ### Using The Notifiable Trait -Notifications may be sent in two ways: using the `notify` method of the `Notifiable` trait or using the `Notification` [facade](/docs/{{version}}/facades). First, let's examine the `Notifiable` trait. This trait is used by the default `App\User` model and contains one method that may be used to send notifications: `notify`. The `notify` method expects to receive a notification instance: +Notifications may be sent in two ways: using the `notify` method of the `Notifiable` trait or using the `Notification` [facade](/docs/{{version}}/facades). First, let's explore using the trait: + + ### Using The Notification Facade -Alternatively, you may send notifications via the `Notification` [facade](/docs/{{version}}facades). This is useful primarily when you need to send a notification to multiple notifiable entities such as a collection of users. To send notifications using the facade, pass all of the notifiable entities and the notification instance to the `send` method: +Alternatively, you may send notifications via the `Notification` [facade](/docs/{{version}}/facades). This is useful primarily when you need to send a notification to multiple notifiable entities such as a collection of users. To send notifications using the facade, pass all of the notifiable entities and the notification instance to the `send` method: Notification::send($users, new InvoicePaid($invoice)); @@ -115,7 +135,7 @@ Once the `ShouldQueue` interface has been added to your notification, you may se $user->notify(new InvoicePaid($invoice)); -If you would like to delay the deliver of the notification, you may chain the `delay` method onto your notification instantiation: +If you would like to delay the delivery of the notification, you may chain the `delay` method onto your notification instantiation: $when = Carbon::now()->addMinutes(10); @@ -127,7 +147,7 @@ If you would like to delay the deliver of the notification, you may chain the `d ### Formatting Mail Messages -If a notification supports being sent as an email, you should define a `toMail` method on the notification class. This method will receive a `$notifiable` entity and should return a `Illuminate\Notifications\Messages\MailMessage` instance. Mail messages may contains lines of text as well as a "call to action". Let's take a look at an example `toMail` method: +If a notification supports being sent as an email, you should define a `toMail` method on the notification class. This method will receive a `$notifiable` entity and should return a `Illuminate\Notifications\Messages\MailMessage` instance. Mail messages may contain lines of text as well as a "call to action". Let's take a look at an example `toMail` method: /** * Get the mail representation of the notification. @@ -140,6 +160,7 @@ If a notification supports being sent as an email, you should define a `toMail` $url = url('/invoice/'.$this->invoice->id); return (new MailMessage) + ->greeting('Hello!') ->line('One of your invoices has been paid!') ->action('View Invoice', $url) ->line('Thank you for using our application!'); @@ -147,10 +168,31 @@ If a notification supports being sent as an email, you should define a `toMail` > {tip} Note we are using `$this->invoice->id` in our `message` method. You may pass any data your notification needs to generate its message into the notification's constructor. -In this example, we register a line of text, a call to action, and then another line of text. These methods provided by the `MailMessage` object make it simple and fast to format small transactional emails. The mail channel will then translate the message components into a nice, responsive HTML email template. Here is an example of an email generated by the `mail` channel: +In this example, we register a greeting, a line of text, a call to action, and then another line of text. These methods provided by the `MailMessage` object make it simple and fast to format small transactional emails. The mail channel will then translate the message components into a nice, responsive HTML email template with a plain-text counterpart. Here is an example of an email generated by the `mail` channel: +> {tip} When sending mail notifications, be sure to set the `name` value in your `config/app.php` configuration file. This value will be used in the header and footer of your mail notification messages. + + +#### Error Messages + +Some notifications inform users of errors, such as a failed invoice payment. You may indicate that a mail message is regarding an error by calling the `error` method when building your message. When using the `error` method on a mail message, the call to action button will be red instead of blue: + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Message + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->error() + ->subject('Notification Subject') + ->line('...'); + } + ### Customizing The Recipient @@ -193,28 +235,105 @@ By default, the email's subject is the class name of the notification formatted { return (new MailMessage) ->subject('Notification Subject') - ->line('...') + ->line('...'); } - -### Error Messages + +### Customizing The Templates -Some notifications inform users of errors, such as a failed invoice payment. You may indicate that a mail message is regarding an error by calling the `error` method when building your message. When using the `error` method on a mail message, the call to action button will be red instead of blue: +You can modify the HTML and plain-text template used by mail notifications by publishing the notification package's resources. After running this command, the mail notification templates will be located in the `resources/views/vendor/notifications` directory: + + php artisan vendor:publish --tag=laravel-notifications + + +## Markdown Mail Notifications + +Markdown mail notifications allow you to take advantage of the pre-built templates of mail notifications, while giving you more freedom to write longer, customized messages. Since the messages are written in Markdown, Laravel is able to render beautiful, responsive HTML templates for the messages while also automatically generating a plain-text counterpart. + + +### Generating The Message + +To generate a notification with a corresponding Markdown template, you may use the `--markdown` option of the `make:notification` Artisan command: + + php artisan make:notification InvoicePaid --markdown=mail.invoice.paid + +Like all other mail notifications, notifications that use Markdown templates should define a `toMail` method on their notification class. However, instead of using the `line` and `action` methods to construct the notification, use the `markdown` method to specify the name of the Markdown template that should be used: /** * Get the mail representation of the notification. * * @param mixed $notifiable - * @return \Illuminate\Notifications\Message + * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable) { + $url = url('/invoice/'.$this->invoice->id); + return (new MailMessage) - ->error() - ->subject('Notification Subject') - ->line('...') + ->subject('Invoice Paid') + ->markdown('mail.invoice.paid', ['url' => $url]); } + +### Writing The Message + +Markdown mail notifications use a combination of Blade components and Markdown syntax which allow you to easily construct notifications while leveraging Laravel's pre-crafted notification components: + + @component('mail::message') + # Invoice Paid + + Your invoice has been paid! + + @component('mail::button', ['url' => $url]) + View Invoice + @endcomponent + + Thanks,
+ {{ config('app.name') }} + @endcomponent + +#### Button Component + +The button component renders a centered button link. The component accepts two arguments, a `url` and an optional `color`. Supported colors are `blue`, `green`, and `red`. You may add as many button components to a notification as you wish: + + @component('mail::button', ['url' => $url, 'color' => 'green']) + View Invoice + @endcomponent + +#### Panel Component + +The panel component renders the given block of text in a panel that has a slightly different background color than the rest of the notification. This allows you to draw attention to a given block of text: + + @component('mail::panel') + This is the panel content. + @endcomponent + +#### Table Component + +The table component allows you to transform a Markdown table into an HTML table. The component accepts the Markdown table as its content. Table column alignment is supported using the default Markdown table alignment syntax: + + @component('mail::table') + | Laravel | Table | Example | + | ------------- |:-------------:| --------:| + | Col 2 is | Centered | $10 | + | Col 3 is | Right-Aligned | $20 | + @endcomponent + + +### Customizing The Components + +You may export all of the Markdown notification components to your own application for customization. To export the components, use the `vendor:publish` Artisan command to publish the `laravel-mail` asset tag: + + php artisan vendor:publish --tag=laravel-mail + +This command will publish the Markdown mail components to the `resources/views/vendor/mail` directory. The `mail` directory will contain a `html` and a `markdown` directory, each containing their respective representations of every available component. You are free to customize these components however you like. + +#### Customizing The CSS + +After exporting the components, the `resources/views/vendor/mail/html/themes` directory will contain a `default.css` file. You may customize the CSS in this file and your styles will automatically be in-lined within the HTML representations of your Markdown notifications. + +> {tip} If you would like to build an entirely new theme for the Markdown components, simply write a new CSS file within the `html/themes` directory and change the `theme` option of your `mail` configuration file. + ## Database Notifications @@ -280,19 +399,19 @@ Typically, you will want to mark a notification as "read" when a user views it. $user = App\User::find(1); - foreach ($user->notifications as $notification) { + foreach ($user->unreadNotifications as $notification) { $notification->markAsRead(); } However, instead of looping through each notification, you may use the `markAsRead` method directly on a collection of notifications: - $user->notifications->markAsRead(); + $user->unreadNotifications->markAsRead(); You may also use a mass-update query to mark all of the notifications as read without retrieving them from the database: $user = App\User::find(1); - $user->notifications()->update(['read_at' => Carbon::now()]); + $user->unreadNotifications()->update(['read_at' => Carbon::now()]); Of course, you may `delete` the notifications to remove them from the table entirely: @@ -309,27 +428,33 @@ Before broadcasting notifications, you should configure and be familiar with Lar ### Formatting Broadcast Notifications -The `broadcast` channel broadcasts notifications using Laravel's [event broadcasting](/docs/{{version}}/broadcasting) services, allowing your JavaScript client to catch notifications in realtime. If a notification supports broadcasting, you should define a `toBroadcast` or `toArray` method on the notification class. This method will receive a `$notifiable` entity and should return a plain PHP array. The returned array will be encoded as JSON and broadcast to your JavaScript client. Let's take a look at an example `toArray` method: +The `broadcast` channel broadcasts notifications using Laravel's [event broadcasting](/docs/{{version}}/broadcasting) services, allowing your JavaScript client to catch notifications in realtime. If a notification supports broadcasting, you should define a `toBroadcast` method on the notification class. This method will receive a `$notifiable` entity and should return a `BroadcastMessage` instance. The returned data will be encoded as JSON and broadcast to your JavaScript client. Let's take a look at an example `toBroadcast` method: + + use Illuminate\Notifications\Messages\BroadcastMessage; /** - * Get the array representation of the notification. + * Get the broadcastable representation of the notification. * * @param mixed $notifiable - * @return array + * @return BroadcastMessage */ - public function toArray($notifiable) + public function toBroadcast($notifiable) { - return [ + return new BroadcastMessage([ 'invoice_id' => $this->invoice->id, 'amount' => $this->invoice->amount, - ]; + ]); } -> {tip} In addition to the data you specify, broadcast notifications will also contain a `type` field containing the class name of the notification. +#### Broadcast Queue Configuration -#### `toBroadcast` Vs. `toArray` +All broadcast notifications are queued for broadcasting. If you would like to configure the queue connection or queue name that is used to the queue the broadcast operation, you may use the `onConnection` and `onQueue` methods of the `BroadcastMessage`: -The `toArray` method is also used by the `database` channel to determine which data to store in your database table. If you would like to have two different array representations for the `database` and `broadcast` channels, you should define a `toBroadcast` method instead of a `toArray` method. + return new BroadcastMessage($data) + ->onConnection('sqs') + ->onQueue('broadcasts'); + +> {tip} In addition to the data you specify, broadcast notifications will also contain a `type` field containing the class name of the notification. ### Listening For Notifications @@ -341,6 +466,35 @@ Notifications will broadcast on a private channel formatted using a `{notifiable console.log(notification.type); }); +#### Customizing The Notification Channel + +If you would like to customize which channels a notifiable entity receives its broadcast notifications on, you may define a `receivesBroadcastNotificationsOn` method on the notifiable entity: + + id), + ]; + } + } + ## SMS Notifications @@ -374,6 +528,23 @@ If a notification supports being sent as a SMS, you should define a `toNexmo` me ->content('Your SMS message content'); } +#### Unicode Content + +If your SMS message will contain unicode characters, you should call the `unicode` method when constructing the `NexmoMessage` instance: + + /** + * Get the Nexmo / SMS representation of the notification. + * + * @param mixed $notifiable + * @return NexmoMessage + */ + public function toNexmo($notifiable) + { + return (new NexmoMessage) + ->content('Your unicode message') + ->unicode(); + } + ### Customizing The "From" Number @@ -452,7 +623,26 @@ In this example we are just sending a single line of text to Slack, which will c -#### Slack Attachments +#### Customizing The Sender & Recipient + +You may use the `from` and `to` methods to customize the sender and recipient. The `from` method accepts a username and emoji identifier, while the `to` method accepts a channel or username: + + /** + * Get the Slack representation of the notification. + * + * @param mixed $notifiable + * @return SlackMessage + */ + public function toSlack($notifiable) + { + return (new SlackMessage) + ->from('Ghost', ':ghost:') + ->to('#other') + ->content('This will be sent to #other'); + } + + +### Slack Attachments You may also add "attachments" to Slack messages. Attachments provide richer formatting options than simple text messages. In this example, we will send an error notification about an exception that occurred in an application, including a link to view more details about the exception: @@ -509,6 +699,30 @@ The example above will create a Slack message that looks like the following: +#### Markdown Attachment Content + +If some of your attachment fields contain Markdown, you may use the `markdown` method to instruct Slack to parse and display the given attachment fields as Markdown formatted text: + + /** + * Get the Slack representation of the notification. + * + * @param mixed $notifiable + * @return SlackMessage + */ + public function toSlack($notifiable) + { + $url = url('/exceptions/'.$this->exception->id); + + return (new SlackMessage) + ->error() + ->content('Whoops! Something went wrong.') + ->attachment(function ($attachment) use ($url) { + $attachment->title('Exception: File Not Found', $url) + ->content('File [background.jpg] was **not found**.') + ->markdown(['title', 'text']); + }); + } + ### Routing Slack Notifications diff --git a/package.json b/package.json new file mode 100644 index 0000000..8e62f29 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "5.4", + "version": "1.0.0", + "description": "## Contribution Guidelines", + "main": "index.js", + "scripts": { + "docs:prepare": "gitbook install", + "docs:watch": "npm run docs:prepare && gitbook serve", + "docs:build": "npm run docs:prepare && rm -rf _book && gitbook build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/setkyar/laravel-docs.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/setkyar/laravel-docs/issues" + }, + "homepage": "https://github.com/setkyar/laravel-docs#readme", + "devDependencies": { + "gitbook-cli": "^2.3.0" + } +} diff --git a/packages.md b/packages.md index 87280ef..45a33dc 100644 --- a/packages.md +++ b/packages.md @@ -7,8 +7,10 @@ - [Resources](#resources) - [Configuration](#configuration) - [Migrations](#migrations) + - [Routes](#routes) - [Translations](#translations) - [Views](#views) +- [Commands](#commands) - [Public Assets](#public-assets) - [Publishing File Groups](#publishing-file-groups) @@ -36,7 +38,7 @@ A service provider extends the `Illuminate\Support\ServiceProvider` class and co ## Routing -To define routes for your package, simply `require` the routes file from within your package service provider's `boot` method. From within your routes file, you may use the `Illuminate\Support\Facades\Route` facade to [register routes](/docs/{{version}}/routing) just as you would within a typical Laravel application: +To define routes for your package, pass the routes file path to the `loadRoutesFrom` method from within your package service provider's `boot` method. From within your routes file, you may use the `Illuminate\Support\Facades\Route` facade to [register routes](/docs/{{version}}/routing) just as you would within a typical Laravel application: /** * Perform post-registration booting of services. @@ -45,9 +47,7 @@ To define routes for your package, simply `require` the routes file from within */ public function boot() { - if (! $this->app->routesAreCached()) { - require __DIR__.'/../../routes.php'; - } + $this->loadRoutesFrom(__DIR__.'/path/to/routes.php'); } @@ -74,6 +74,8 @@ Now, when users of your package execute Laravel's `vendor:publish` command, your $value = config('courier.option'); +> {note} You should not define Closures in your configuration files. They can not be serialized correctly when users execute the `config:cache` Artisan command. + #### Default Package Configuration You may also merge your own package configuration file with the application's published copy. This will allow your users to define only the options they actually want to override in the published copy of the configuration. To merge the configurations, use the `mergeConfigFrom` method within your service provider's `register` method: @@ -90,6 +92,23 @@ You may also merge your own package configuration file with the application's pu ); } +> {note} This method only merges the first level of the configuration array. If your users partially define a multi-dimensional configuration array, the missing options will not be merged. + + +### Routes + +If your package contains routes, you may load them using the `loadRoutesFrom` method. This method will automatically determine if the application's routes are cached and will not load your routes file if the routes have already been cached: + + /** + * Perform post-registration booting of services. + * + * @return void + */ + public function boot() + { + $this->loadRoutesFrom(__DIR__.'/routes.php'); + } + ### Migrations @@ -191,6 +210,26 @@ If you would like to make your views available for publishing to the application Now, when users of your package execute Laravel's `vendor:publish` Artisan command, your package's views will be copied to the specified publish location. + +## Commands + +To register your package's Artisan commands with Laravel, you may use the `commands` method. This method expects an array of command class names. Once the commands have been registered, you may execute them using the [Artisan CLI](/docs/{{version}}/artisan): + + /** + * Bootstrap the application services. + * + * @return void + */ + public function boot() + { + if ($this->app->runningInConsole()) { + $this->commands([ + FooCommand::class, + BarCommand::class, + ]); + } + } + ## Public Assets @@ -235,4 +274,4 @@ You may want to publish groups of package assets and resources separately. For i Now your users may publish these groups separately by referencing their tag when executing the `vendor:publish` command: - php artisan vendor:publish --tag="config" + php artisan vendor:publish --tag=config diff --git a/pagination.md b/pagination.md index 77444c2..2a28f67 100644 --- a/pagination.md +++ b/pagination.md @@ -13,7 +13,7 @@ ## Introduction -In other frameworks, pagination can be very painful. Laravel's paginator is integrated with the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent) and provides convenient, easy-to-use pagination of database results out of the box. The HTML generated by the paginator is compatible with the [Bootstrap CSS framework](http://getbootstrap.com/). +In other frameworks, pagination can be very painful. Laravel's paginator is integrated with the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent) and provides convenient, easy-to-use pagination of database results out of the box. The HTML generated by the paginator is compatible with the [Bootstrap CSS framework](https://getbootstrap.com/). ## Basic Usage @@ -79,7 +79,7 @@ The `Paginator` class does not need to know the total number of items in the res In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder and Eloquent, while the `LengthAwarePaginator` corresponds to the `paginate` method. -> {note} When manually creating a paginator instance, you should manually "slice" the array of results you pass to the paginator. If you're unsure how to do this, check out the [array_slice](http://php.net/manual/en/function.array-slice.php) PHP function. +> {note} When manually creating a paginator instance, you should manually "slice" the array of results you pass to the paginator. If you're unsure how to do this, check out the [array_slice](https://secure.php.net/manual/en/function.array-slice.php) PHP function. ## Displaying Pagination Results @@ -98,12 +98,12 @@ The `links` method will render the links to the rest of the pages in the result #### Customizing The Paginator URI -The `setPath` method allows you to customize the URI used by the paginator when generating links. For example, if you want the paginator to generate links like `http://example.com/custom/url?page=N`, you should pass `custom/url` to the `setPath` method: +The `withPath` method allows you to customize the URI used by the paginator when generating links. For example, if you want the paginator to generate links like `http://example.com/custom/url?page=N`, you should pass `custom/url` to the `withPath` method: Route::get('users', function () { $users = App\User::paginate(15); - $users->setPath('custom/url'); + $users->withPath('custom/url'); // }); @@ -121,7 +121,7 @@ If you wish to append a "hash fragment" to the paginator's URLs, you may use the ### Converting Results To JSON -The Laravel paginator result classes implement the `Illuminate\Contracts\Support\JsonableInterface` contract and expose the `toJson` method, so it's very easy to convert your pagination results to JSON. You may also convert a paginator instance to JSON by simply returning it from a route or controller action: +The Laravel paginator result classes implement the `Illuminate\Contracts\Support\Jsonable` Interface contract and expose the `toJson` method, so it's very easy to convert your pagination results to JSON. You may also convert a paginator instance to JSON by simply returning it from a route or controller action: Route::get('users', function () { return App\User::paginate(); @@ -155,6 +155,9 @@ By default, the views rendered to display the pagination links are compatible wi {{ $paginator->links('view.name') }} + // Passing data to the view... + {{ $paginator->links('view.name', ['foo' => 'bar']) }} + However, the easiest way to customize the pagination views is by exporting them to your `resources/views/vendor` directory using the `vendor:publish` command: php artisan vendor:publish --tag=laravel-pagination diff --git a/api-authentication.md b/passport.md similarity index 71% rename from api-authentication.md rename to passport.md index 623d992..356faad 100644 --- a/api-authentication.md +++ b/passport.md @@ -2,15 +2,22 @@ - [Introduction](#introduction) - [Installation](#installation) - - [Configuration](#configuration) - [Frontend Quickstart](#frontend-quickstart) -- [OAuth2 Using Authorization Codes](#oauth2-using-authorization-codes) +- [Configuration](#configuration) + - [Token Lifetimes](#token-lifetimes) +- [Issuing Access Tokens](#issuing-access-tokens) - [Managing Clients](#managing-clients) - [Requesting Tokens](#requesting-tokens) - [Refreshing Tokens](#refreshing-tokens) +- [Password Grant Tokens](#password-grant-tokens) + - [Creating A Password Grant Client](#creating-a-password-grant-client) + - [Requesting Tokens](#requesting-password-grant-tokens) + - [Requesting All Scopes](#requesting-all-scopes) +- [Implicit Grant Tokens](#implicit-grant-tokens) +- [Client Credentials Grant Tokens](#client-credentials-grant-tokens) - [Personal Access Tokens](#personal-access-tokens) - - [Creating A Personal Access Client](#) - - [Managing Personal Access Tokens](#) + - [Creating A Personal Access Client](#creating-a-personal-access-client) + - [Managing Personal Access Tokens](#managing-personal-access-tokens) - [Protecting Routes](#protecting-routes) - [Via Middleware](#via-middleware) - [Passing The Access Token](#passing-the-access-token) @@ -19,6 +26,7 @@ - [Assigning Scopes To Tokens](#assigning-scopes-to-tokens) - [Checking Scopes](#checking-scopes) - [Consuming Your API With JavaScript](#consuming-your-api-with-javascript) +- [Events](#events) ## Introduction @@ -42,7 +50,9 @@ The Passport service provider registers its own database migration directory wit php artisan migrate -Next, you should run the `passport:install` command. This command will create the encryption keys needed to generate secure access tokens. In addition, the command will create a "personal access" client which will be used to generate personal access tokens: +> {note} If you are not going to use Passport's default migrations, you should call the `Passport::ignoreMigrations` method in the `register` method of your `AppServiceProvider`. You may export the default migrations using `php artisan vendor:publish --tag=passport-migrations`. + +Next, you should run the `passport:install` command. This command will create the encryption keys needed to generate secure access tokens. In addition, the command will create "personal access" and "password grant" clients which will be used to generate access tokens: php artisan passport:install @@ -109,29 +119,6 @@ Finally, in your `config/auth.php` configuration file, you should set the `drive ], ], - -### Configuration - -#### Token Lifetimes - -By default, Passport issues long-lived access tokens that never need to be refreshed. If you would like to configure a shorter token lifetime, you may use the `tokensExpireIn` and `refreshTokensExpireIn` methods. These methods should be called from the `boot` method of your `AuthServiceProvider`: - - /** - * Register any authentication / authorization services. - * - * @return void - */ - public function boot() - { - $this->registerPolicies(); - - Passport::routes(); - - Passport::tokensExpireIn(Carbon::now()->addDays(15)); - - Passport::refreshTokensExpireIn(Carbon::now()->addDays(30)); - } - ### Frontend Quickstart @@ -160,14 +147,40 @@ The published components will be placed in your `resources/assets/js/components` require('./components/passport/PersonalAccessTokens.vue') ); -Once the components have been registered, you may drop them into one of your application's templates to get started creating clients and personal access tokens: +After registering the components, make sure to run `npm run dev` to recompile your assets. Once you have recompiled your assets, you may drop the components into one of your application's templates to get started creating clients and personal access tokens: - -## OAuth2 Using Authorization Codes + +## Configuration + + +### Token Lifetimes + +By default, Passport issues long-lived access tokens that never need to be refreshed. If you would like to configure a shorter token lifetime, you may use the `tokensExpireIn` and `refreshTokensExpireIn` methods. These methods should be called from the `boot` method of your `AuthServiceProvider`: + + use Carbon\Carbon; + + /** + * Register any authentication / authorization services. + * + * @return void + */ + public function boot() + { + $this->registerPolicies(); + + Passport::routes(); + + Passport::tokensExpireIn(Carbon::now()->addDays(15)); + + Passport::refreshTokensExpireIn(Carbon::now()->addDays(30)); + } + + +## Issuing Access Tokens Using OAuth2 with authorization codes is how most developers are familiar with OAuth2. When using authorization codes, a client application will redirect a user to your server where they will either approve or deny the request to issue an access token to the client. @@ -186,15 +199,15 @@ The simplest way to create a client is using the `passport:client` Artisan comma Since your users will not be able to utilize the `client` command, Passport provides a JSON API that you may use to create clients. This saves you the trouble of having to manually code controllers for creating, updating, and deleting clients. -However, you will need to pair Passport's JSON API with your own frontend to provide a dashboard for your users to manage their clients. Below, we'll review all of the API endpoints for managing clients. For convenience, we'll use [Vue](https://vuejs.org) to demonstrate making HTTP requests to the endpoints. +However, you will need to pair Passport's JSON API with your own frontend to provide a dashboard for your users to manage their clients. Below, we'll review all of the API endpoints for managing clients. For convenience, we'll use [Axios](https://github.com/mzabriskie/axios) to demonstrate making HTTP requests to the endpoints. -> {tip} The routes to send requests to the Passport controllers were registered when you called the `Passport::routes` method in your `AuthServiceProvider`. +> {tip} If you don't want to implement the entire client management frontend yourself, you can use the [frontend quickstart](#frontend-quickstart) to have a fully functional frontend in a matter of minutes. #### `GET /oauth/clients` This route returns all of the clients for the authenticated user. This is primarily useful for listing all of the user's clients so that they may edit or delete them: - this.$http.get('/oauth/clients') + axios.get('/oauth/clients') .then(response => { console.log(response.data); }); @@ -210,7 +223,7 @@ When a client is created, it will be issued a client ID and client secret. These redirect: 'http://example.com/callback' }; - this.$http.post('/oauth/clients', data) + axios.post('/oauth/clients', data) .then(response => { console.log(response.data); }) @@ -227,7 +240,7 @@ This route is used to update clients. It requires two pieces of data: the client redirect: 'http://example.com/callback' }; - this.$http.put('/oauth/clients/' + clientId, data) + axios.put('/oauth/clients/' + clientId, data) .then(response => { console.log(response.data); }) @@ -239,7 +252,7 @@ This route is used to update clients. It requires two pieces of data: the client This route is used to delete clients: - this.$http.delete('/oauth/clients/' + clientId) + axios.delete('/oauth/clients/' + clientId) .then(response => { // }); @@ -249,7 +262,7 @@ This route is used to delete clients: #### Redirecting For Authorization -Once a client has been created, developer's may use their client ID and secret to request an authorization code and access token from your application. First, the consuming application should make a redirect request to your application's `/oauth/authorize` route like so: +Once a client has been created, developers may use their client ID and secret to request an authorization code and access token from your application. First, the consuming application should make a redirect request to your application's `/oauth/authorize` route like so: Route::get('/redirect', function () { $query = http_build_query([ @@ -274,7 +287,7 @@ If you would like to customize the authorization approval screen, you may publis #### Converting Authorization Codes To Access Tokens -If the user approves the authorization request, they will be redirected back to the consuming application. The consumer should then issue a `POST` request to your application to request an access token. The request should include the authorization code that was issued by when the user approved the authorization request. In this example, we'll use the Guzzle HTTP library to make the `POST` request: +If the user approves the authorization request, they will be redirected back to the consuming application. The consumer should then issue a `POST` request to your application to request an access token. The request should include the authorization code that was issued by your application when the user approved the authorization request. In this example, we'll use the Guzzle HTTP library to make the `POST` request: Route::get('/callback', function (Request $request) { $http = new GuzzleHttp\Client; @@ -317,6 +330,108 @@ If your application issues short-lived access tokens, users will need to refresh This `/oauth/token` route will return a JSON response containing `access_token`, `refresh_token`, and `expires_in` attributes. The `expires_in` attribute contains the number of seconds until the access token expires. + +## Password Grant Tokens + +The OAuth2 password grant allows your other first-party clients, such as a mobile application, to obtain an access token using an e-mail address / username and password. This allows you to issue access tokens securely to your first-party clients without requiring your users to go through the entire OAuth2 authorization code redirect flow. + + +### Creating A Password Grant Client + +Before your application can issue tokens via the password grant, you will need to create a password grant client. You may do this using the `passport:client` command with the `--password` option. If you have already run the `passport:install` command, you do not need to run this command: + + php artisan passport:client --password + + +### Requesting Tokens + +Once you have created a password grant client, you may request an access token by issuing a `POST` request to the `/oauth/token` route with the user's email address and password. Remember, this route is already registered by the `Passport::routes` method so there is no need to define it manually. If the request is successful, you will receive an `access_token` and `refresh_token` in the JSON response from the server: + + $http = new GuzzleHttp\Client; + + $response = $http->post('http://your-app.com/oauth/token', [ + 'form_params' => [ + 'grant_type' => 'password', + 'client_id' => 'client-id', + 'client_secret' => 'client-secret', + 'username' => 'taylor@laravel.com', + 'password' => 'my-password', + 'scope' => '', + ], + ]); + + return json_decode((string) $response->getBody(), true); + +> {tip} Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed. + + +### Requesting All Scopes + +When using the password grant, you may wish to authorize the token for all of the scopes supported by your application. You can do this by requesting the `*` scope. If you request the `*` scope, the `can` method on the token instance will always return `true`. This scope may only be assigned to a token that is issued using the `password` grant: + + $response = $http->post('http://your-app.com/oauth/token', [ + 'form_params' => [ + 'grant_type' => 'password', + 'client_id' => 'client-id', + 'client_secret' => 'client-secret', + 'username' => 'taylor@laravel.com', + 'password' => 'my-password', + 'scope' => '*', + ], + ]); + + +## Implicit Grant Tokens + +The implicit grant is similar to the authorization code grant; however, the token is returned to the client without exchanging an authorization code. This grant is most commonly used for JavaScript or mobile applications where the client credentials can't be securely stored. To enable the grant, call the `enableImplicitGrant` method in your `AuthServiceProvider`: + + /** + * Register any authentication / authorization services. + * + * @return void + */ + public function boot() + { + $this->registerPolicies(); + + Passport::routes(); + + Passport::enableImplicitGrant(); + } + +Once a grant has been enabled, developers may use their client ID to request an access token from your application. The consuming application should make a redirect request to your application's `/oauth/authorize` route like so: + + Route::get('/redirect', function () { + $query = http_build_query([ + 'client_id' => 'client-id', + 'redirect_uri' => 'http://example.com/callback', + 'response_type' => 'token', + 'scope' => '', + ]); + + return redirect('http://your-app.com/oauth/authorize?'.$query); + }); + +> {tip} Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route. + + +## Client Credentials Grant Tokens + +The client credentials grant is suitable for machine-to-machine authentication. For example, you might use this grant in a scheduled job which is performing maintenance tasks over an API. To retrieve a token, make a request to the `oauth/token` endpoint: + + $guzzle = new GuzzleHttp\Client; + + $response = $guzzle->post('http://your-app.com/oauth/token', [ + 'form_params' => [ + 'grant_type' => 'client_credentials', + 'client_id' => 'client-id', + 'client_secret' => 'client-secret', + 'scope' => 'your-scope', + ], + ]); + + echo json_decode((string) $response->getBody(), true); + ## Personal Access Tokens @@ -346,13 +461,15 @@ Once you have created a personal access client, you may issue tokens for a given #### JSON API -Passport also includes a JSON API for managing personal access tokens. You may pair this with your own frontend to offer your users a dashboard for managing personal access tokens. Below, we'll review all of the API endpoints for managing personal access tokens. For convenience, we'll use [Vue](https://vuejs.org) to demonstrate making HTTP requests to the endpoints. +Passport also includes a JSON API for managing personal access tokens. You may pair this with your own frontend to offer your users a dashboard for managing personal access tokens. Below, we'll review all of the API endpoints for managing personal access tokens. For convenience, we'll use [Axios](https://github.com/mzabriskie/axios) to demonstrate making HTTP requests to the endpoints. + +> {tip} If you don't want to implement the personal access token frontend yourself, you can use the [frontend quickstart](#frontend-quickstart) to have a fully functional frontend in a matter of minutes. #### `GET /oauth/scopes` -This route returns all of the [scopes](#scopes) defined for your application. You may use this route to list the scopes a user may assign to a personal access token: +This route returns all of the [scopes](#token-scopes) defined for your application. You may use this route to list the scopes a user may assign to a personal access token: - this.$http.get('/oauth/scopes') + axios.get('/oauth/scopes') .then(response => { console.log(response.data); }); @@ -361,7 +478,7 @@ This route returns all of the [scopes](#scopes) defined for your application. Yo This route returns all of the personal access tokens that the authenticated user has created. This is primarily useful for listing all of the user's token so that they may edit or delete them: - this.$http.get('/oauth/personal-access-tokens') + axios.get('/oauth/personal-access-tokens') .then(response => { console.log(response.data); }); @@ -375,7 +492,7 @@ This route creates new personal access tokens. It requires two pieces of data: t scopes: [] }; - this.$http.post('/oauth/personal-access-tokens', data) + axios.post('/oauth/personal-access-tokens', data) .then(response => { console.log(response.data.accessToken); }) @@ -387,7 +504,7 @@ This route creates new personal access tokens. It requires two pieces of data: t This route may be used to delete personal access tokens: - this.$http.delete('/oauth/personal-access-tokens/' + tokenId); + axios.delete('/oauth/personal-access-tokens/' + tokenId); ## Protecting Routes @@ -401,8 +518,6 @@ Passport includes an [authentication guard](/docs/{{version}}/authentication#add // })->middleware('auth:api'); -> {tip} The `RouteServiceProvider` included with fresh Laravel applications already loads the `api` route file within a route group containing the `auth:api` middleware. So, there is no need to manually assign this middleware routes in your `api` routes file. - ### Passing The Access Token @@ -507,17 +622,38 @@ Typically, if you want to consume your API from your JavaScript application, you This Passport middleware will attach a `laravel_token` cookie to your outgoing responses. This cookie contains an encrypted JWT that Passport will use to authenticate API requests from your JavaScript application. Now, you may make requests to your application's API without explicitly passing an access token: - this.$http.get('/user') + axios.get('/user') .then(response => { console.log(response.data); }); -When using this method of authentication, you will need to send the CSRF token with every request via the `X-CSRF-TOKEN` header. Laravel will automatically send this header if you are using the default [Vue](https://vuejs.org) configuration that is included with the framework: +When using this method of authentication, Axios will automatically send the `X-CSRF-TOKEN` header. In addition, the default Laravel JavaScript scaffolding instructs Axios to send the `X-Requested-With` header: - Vue.http.interceptors.push((request, next) => { - request.headers['X-CSRF-TOKEN'] = Laravel.csrfToken; + window.axios.defaults.headers.common = { + 'X-Requested-With': 'XMLHttpRequest', + }; - next(); - }); +> {note} If you are using a different JavaScript framework, you should make sure it is configured to send the `X-CSRF-TOKEN` and `X-Requested-With` headers with every outgoing request. + + + +## Events + +Passport raises events when issuing access tokens and refresh tokens. You may use these events to prune or revoke other access tokens in your database. You may attach listeners to these events in your application's `EventServiceProvider`: -> {note} If you are using a different JavaScript framework, you should make sure it is configured to send this header with every outgoing request. +```php +/** + * The event listener mappings for the application. + * + * @var array + */ +protected $listen = [ + 'Laravel\Passport\Events\AccessTokenCreated' => [ + 'App\Listeners\RevokeOldTokens', + ], + + 'Laravel\Passport\Events\RefreshTokenCreated' => [ + 'App\Listeners\PruneOldTokens', + ], +]; +``` diff --git a/passwords.md b/passwords.md index 51535b9..0a8beee 100644 --- a/passwords.md +++ b/passwords.md @@ -68,6 +68,8 @@ In your `auth.php` configuration file, you may configure multiple "guards", whic In your `auth.php` configuration file, you may configure multiple password "brokers", which may be used to reset passwords on multiple user tables. You can customize the included `ForgotPasswordController` and `ResetPasswordController` to use the broker of your choice by overriding the `broker` method: + use Illuminate\Support\Facades\Password; + /** * Get the broker to be used during password reset. * @@ -77,3 +79,19 @@ In your `auth.php` configuration file, you may configure multiple password "brok { return Password::broker('name'); } + +#### Reset Email Customization + +You may easily modify the notification class used to send the password reset link to the user. To get started, override the `sendPasswordResetNotification` method on your `User` model. Within this method, you may send the notification using any notification class you choose. The password reset `$token` is the first argument received by the method: + + /** + * Send the password reset notification. + * + * @param string $token + * @return void + */ + public function sendPasswordResetNotification($token) + { + $this->notify(new ResetPasswordNotification($token)); + } + diff --git a/providers.md b/providers.md index f8107c8..1f59fa0 100644 --- a/providers.md +++ b/providers.md @@ -30,7 +30,7 @@ The Artisan CLI can generate a new provider via the `make:provider` command: ### The Register Method -As mentioned previously, within the `register` method, you should only bind things into the [service container](/docs/{{version}}/container). You should never attempt to register any event listeners, routes, or any other piece of functionality within the `register` method. Otherwise, you may accidently use a service that is provided by a service provider which has not loaded yet. +As mentioned previously, within the `register` method, you should only bind things into the [service container](/docs/{{version}}/container). You should never attempt to register any event listeners, routes, or any other piece of functionality within the `register` method. Otherwise, you may accidentally use a service that is provided by a service provider which has not loaded yet. Let's take a look at a basic service provider. Within any of your service provider methods, you always have access to the `$app` property which provides access to the service container: diff --git a/queries.md b/queries.md index 27c5e8a..0458680 100644 --- a/queries.md +++ b/queries.md @@ -77,7 +77,7 @@ If you don't even need an entire row, you may extract a single value from a reco #### Retrieving A List Of Column Values -If you would like to retrieve an array containing the values of a single column, you may use the `pluck` method. In this example, we'll retrieve an array of role titles: +If you would like to retrieve a Collection containing the values of a single column, you may use the `pluck` method. In this example, we'll retrieve a Collection of role titles: $titles = DB::table('roles')->pluck('title'); @@ -85,7 +85,7 @@ If you would like to retrieve an array containing the values of a single column, echo $title; } - You may also specify a custom key column for the returned array: + You may also specify a custom key column for the returned Collection: $roles = DB::table('roles')->pluck('title', 'name'); @@ -98,7 +98,7 @@ If you would like to retrieve an array containing the values of a single column, If you need to work with thousands of database records, consider using the `chunk` method. This method retrieves a small chunk of the results at a time and feeds each chunk into a `Closure` for processing. This method is very useful for writing [Artisan commands](/docs/{{version}}/artisan) that process thousands of records. For example, let's work with the entire `users` table in chunks of 100 records at a time: - DB::table('users')->orderBy('id')->chunk(100, function($users) { + DB::table('users')->orderBy('id')->chunk(100, function ($users) { foreach ($users as $user) { // } @@ -106,7 +106,7 @@ If you need to work with thousands of database records, consider using the `chun You may stop further chunks from being processed by returning `false` from the `Closure`: - DB::table('users')->orderBy('id')->chunk(100, function($users) { + DB::table('users')->orderBy('id')->chunk(100, function ($users) { // Process the records... return false; @@ -312,25 +312,25 @@ The `whereNotNull` method verifies that the column's value is not `NULL`: **whereDate / whereMonth / whereDay / whereYear** -The `whereDate` method may be used compare a column's value against a date: +The `whereDate` method may be used to compare a column's value against a date: $users = DB::table('users') - ->whereDate('created_at', '2016-10-10') + ->whereDate('created_at', '2016-12-31') ->get(); -The `whereMonth` method may be used compare a column's value against a specific month of an year: +The `whereMonth` method may be used to compare a column's value against a specific month of a year: $users = DB::table('users') - ->whereMonth('created_at', '10') + ->whereMonth('created_at', '12') ->get(); -The `whereDay` method may be used compare a column's value against a specific day of a month: +The `whereDay` method may be used to compare a column's value against a specific day of a month: $users = DB::table('users') - ->whereDay('created_at', '10') + ->whereDay('created_at', '31') ->get(); -The `whereYear` method may be used compare a column's value against a specific year: +The `whereYear` method may be used to compare a column's value against a specific year: $users = DB::table('users') ->whereYear('created_at', '2016') @@ -419,6 +419,14 @@ The `orderBy` method allows you to sort the result of the query by a given colum ->orderBy('name', 'desc') ->get(); +#### latest / oldest + +The `latest` and `oldest` methods allow you to easily order results by date. By default, result will be ordered by the `created_at` column. Or, you may pass the column name that you wish to sort by: + + $user = DB::table('users') + ->latest() + ->first(); + #### inRandomOrder The `inRandomOrder` method may be used to sort the query results randomly. For example, you may use this method to fetch a random user: @@ -450,6 +458,13 @@ To limit the number of results returned from the query, or to skip a given numbe $users = DB::table('users')->skip(10)->take(5)->get(); +Alternatively, you may use the `limit` and `offset` methods: + + $users = DB::table('users') + ->offset(10) + ->limit(5) + ->get(); + ## Conditional Clauses @@ -466,6 +481,19 @@ Sometimes you may want clauses to apply to a query only when something else is t The `when` method only executes the given Closure when the first parameter is `true`. If the first parameter is `false`, the Closure will not be executed. +You may pass another Closure as the third parameter to the `when` method. This Closure will execute if the first parameter evaluates as `false`. To illustrate how this feature may be used, we will use it to configure the default sorting of a query: + + $sortBy = null; + + $users = DB::table('users') + ->when($sortBy, function ($query) use ($sortBy) { + return $query->orderBy($sortBy); + }, function ($query) { + return $query->orderBy('name'); + }) + ->get(); + + ## Inserts @@ -513,7 +541,7 @@ When updating a JSON column, you should use `->` syntax to access the appropriat ### Increment & Decrement -The query builder also provides convenient methods for incrementing or decrementing the value of a given column. This is simply a short-cut, providing a more expressive and terse interface compared to manually writing the `update` statement. +The query builder also provides convenient methods for incrementing or decrementing the value of a given column. This is simply a shortcut, providing a more expressive and terse interface compared to manually writing the `update` statement. Both of these methods accept at least one argument: the column to modify. A second argument may optionally be passed to control the amount by which the column should be incremented or decremented: diff --git a/queues.md b/queues.md index 6b95928..34123b6 100644 --- a/queues.md +++ b/queues.md @@ -9,6 +9,7 @@ - [Dispatching Jobs](#dispatching-jobs) - [Delayed Dispatching](#delayed-dispatching) - [Customizing The Queue & Connection](#customizing-the-queue-and-connection) + - [Specifying Max Job Attempts / Timeout Values](#max-job-attempts-and-timeout) - [Error Handling](#error-handling) - [Running The Queue Worker](#running-the-queue-worker) - [Queue Priorities](#queue-priorities) @@ -26,7 +27,7 @@ Laravel queues provide a unified API across a variety of different queue backends, such as Beanstalk, Amazon SQS, Redis, or even a relational database. Queues allow you to defer the processing of a time consuming task, such as sending an email, until a later time. Deferring these time consuming tasks drastically speeds up web requests to your application. -The queue configuration file is stored in `config/queue.php`. In this file you will find connection configurations for each of the queue drivers that are included with the framework, which includes a database, [Beanstalkd](http://kr.github.com/beanstalkd), [Amazon SQS](http://aws.amazon.com/sqs), [Redis](http://redis.io), and synchronous (for local use) driver. A `null` queue driver is also included which simply discards queued jobs. +The queue configuration file is stored in `config/queue.php`. In this file you will find connection configurations for each of the queue drivers that are included with the framework, which includes a database, [Beanstalkd](https://kr.github.io/beanstalkd/), [Amazon SQS](https://aws.amazon.com/sqs/), [Redis](http://redis.io), and a synchronous driver that will execute jobs immediately (for local use). A `null` queue driver is also included which simply discards queued jobs. ### Connections Vs. Queues @@ -41,7 +42,7 @@ Note that each connection configuration example in the `queue` configuration fil // This job is sent to the "emails" queue... dispatch((new Job)->onQueue('emails')); -Some applications may not need to ever push jobs onto multiples queues, instead preferring to have one simple queue. However, pushing jobs to multiple queues can be especially useful for applications that wish to prioritize or segment how jobs are processed, since the Laravel queue worker allows you to specify which queues it should process by priority. For example, if you push jobs to a `high` queue, you may run a worker that gives them higher processing priority: +Some applications may not need to ever push jobs onto multiple queues, instead preferring to have one simple queue. However, pushing jobs to multiple queues can be especially useful for applications that wish to prioritize or segment how jobs are processed, since the Laravel queue worker allows you to specify which queues it should process by priority. For example, if you push jobs to a `high` queue, you may run a worker that gives them higher processing priority: php artisan queue:work --queue=high,default @@ -56,6 +57,19 @@ In order to use the `database` queue driver, you will need a database table to h php artisan migrate +#### Redis + +In order to use the `redis` queue driver, you should configure a Redis database connection in your `config/database.php` configuration file. + +If your Redis queue connection uses a Redis Cluster, your queue names must contain a [key hash tag](https://redis.io/topics/cluster-spec#keys-hash-tags). This is required in order to ensure all of the Redis keys for a given queue are placed into the same hash slot: + + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default', + 'queue' => '{default}', + 'retry_after' => 90, + ], + #### Other Driver Prerequisites The following dependencies are needed for the listed queue drivers: @@ -76,7 +90,7 @@ By default, all of the queueable jobs for your application are stored in the `ap php artisan make:job SendReminderEmail -The generated class will implement the `Illuminate\Contracts\Queue\ShouldQueue` interface, indicating to Laravel that the job should be pushed onto the queue instead of run synchronously. +The generated class will implement the `Illuminate\Contracts\Queue\ShouldQueue` interface, indicating to Laravel that the job should be pushed onto the queue to run asynchronously. ### Class Structure @@ -89,6 +103,7 @@ Job classes are very simple, normally containing only a `handle` method which is use App\Podcast; use App\AudioProcessor; + use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; @@ -126,6 +141,8 @@ In this example, note that we were able to pass an [Eloquent model](/docs/{{vers The `handle` method is called when the job is processed by the queue. Note that we are able to type-hint dependencies on the `handle` method of the job. The Laravel [service container](/docs/{{version}}/container) automatically injects these dependencies. +> {note} Binary data, such as raw image contents, should be passed through the `base64_encode` function before being passed to a queued job. Otherwise, the job may not properly serialize to JSON when being placed on the queue. + ## Dispatching Jobs @@ -183,7 +200,7 @@ If you would like to delay the execution of a queued job, you may use the `delay { // Create podcast... - $job = (new ProcessPodcast($pocast)) + $job = (new ProcessPodcast($podcast)) ->delay(Carbon::now()->addMinutes(10)); dispatch($job); @@ -261,21 +278,68 @@ Of course, you may chain the `onConnection` and `onQueue` methods to specify the ->onConnection('sqs') ->onQueue('processing'); + +### Specifying Max Job Attempts / Timeout Values + +#### Max Attempts + +One approach to specifying the maximum number of times a job may be attempted is via the `--tries` switch on the Artisan command line: + + php artisan queue:work --tries=3 + +However, you may take a more granular approach by defining the maximum number of attempts on the job class itself. If the maximum number of attempts is specified on the job, it will take precedence over the value provided on the command line: + + ### Error Handling -If an exception is thrown while the job is being processed, the job will automatically be released back onto the queue so it may be attempted again. The job will continue to be released until it has been attempted the maximum number of times allowed by your application. The number of maximum attempts is defined by the `--tries` switch used on the `queue:work` Artisan command. More information on running the queue worker [can be found below](#running-the-queue-worker). +If an exception is thrown while the job is being processed, the job will automatically be released back onto the queue so it may be attempted again. The job will continue to be released until it has been attempted the maximum number of times allowed by your application. The maximum number of attempts is defined by the `--tries` switch used on the `queue:work` Artisan command. Alternatively, the maximum number of attempts may be defined on the job class itself. More information on running the queue worker [can be found below](#running-the-queue-worker). ## Running The Queue Worker -Laravel includes an queue worker that will process new jobs as they are pushed onto the queue. You may run the worker using the `queue:work` Artisan command. Note that once the `queue:work` command has started, it will continue to run until it is manually stopped or you close your terminal: +Laravel includes a queue worker that will process new jobs as they are pushed onto the queue. You may run the worker using the `queue:work` Artisan command. Note that once the `queue:work` command has started, it will continue to run until it is manually stopped or you close your terminal: php artisan queue:work > {tip} To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. -Remember, queue workers are long lived processes and will not notice changes in your code base after they have been started. So, during your deployment process, be sure to [restart your queue workers](#queue-workers-and-deployment). +Remember, queue workers are long-lived processes and store the booted application state in memory. As a result, they will not notice changes in your code base after they have been started. So, during your deployment process, be sure to [restart your queue workers](#queue-workers-and-deployment). #### Specifying The Connection & Queue @@ -289,7 +353,7 @@ You may customize your queue worker even further by only processing particular q #### Resource Considerations -Queue workers are daemons and do not restart the framework before processing each job. Therefore, you should be careful to free any heavy resources before your job finishes. For example, if you are doing image manipulation with the GD library, you should free the memory with `imagedestroy` when you are done. +Daemon queue workers do not "reboot" the framework before processing each job. Therefore, you should free any heavy resources after each job completes. For example, if you are doing image manipulation with the GD library, you should free the memory with `imagedestroy` when you are done. ### Queue Priorities @@ -305,11 +369,11 @@ To start a worker that verifies that all of the `high` queue jobs are processed ### Queue Workers & Deployment -Since queue workers are long lived processes, they will not pick up changes to your code without being restarted. So, the simplest way to deploy an application using queue workers is to restart the workers during your deployment process. You may gracefully restart all of the workers by issuing the `queue:restart` command: +Since queue workers are long-lived processes, they will not pick up changes to your code without being restarted. So, the simplest way to deploy an application using queue workers is to restart the workers during your deployment process. You may gracefully restart all of the workers by issuing the `queue:restart` command: php artisan queue:restart -This command will gracefully instruct all queue workers to "die" after they finish processing their current job so that no existing jobs are lost. Since the queue workers will die when the `queue:restart` command is executed, you should be running a process manager such as [Supervisor](#supervisor-configuration) which automatically restarts the queue workers. +This command will instruct all queue workers to gracefully "die" after they finish processing their current job so that no existing jobs are lost. Since the queue workers will die when the `queue:restart` command is executed, you should be running a process manager such as [Supervisor](#supervisor-configuration) to automatically restart the queue workers. ### Job Expirations & Timeouts @@ -318,18 +382,24 @@ This command will gracefully instruct all queue workers to "die" after they fini In your `config/queue.php` configuration file, each queue connection defines a `retry_after` option. This option specifies how many seconds the queue connection should wait before retrying a job that is being processed. For example, if the value of `retry_after` is set to `90`, the job will be released back onto the queue if it has been processing for 90 seconds without being deleted. Typically, you should set the `retry_after` value to the maximum number of seconds your jobs should reasonably take to complete processing. -> {note} The only queue connection which does not contain a `retry_after` value is Amazon SQS. When using SQS, you must configure the retry threshold from the Amazon web console. +> {note} The only queue connection which does not contain a `retry_after` value is Amazon SQS. SQS will retry the job based on the [Default Visibility Timeout](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html) which is managed within the AWS console. #### Worker Timeouts -The `queue:work` Artisan command exposes a `--timeout` option. The `--timeout` option specifies how long the Laravel queue master process will wait before killing off a child queue worker that is processing a job. Sometimes a child queue process can become "frozen" for various reasons, such as a external HTTP call that is not responding. The `--timeout` option removes frozen processes that have exceeded that specified time limit: +The `queue:work` Artisan command exposes a `--timeout` option. The `--timeout` option specifies how long the Laravel queue master process will wait before killing off a child queue worker that is processing a job. Sometimes a child queue process can become "frozen" for various reasons, such as an external HTTP call that is not responding. The `--timeout` option removes frozen processes that have exceeded that specified time limit: - php artisan queue:listen --timeout=60 + php artisan queue:work --timeout=60 The `retry_after` configuration option and the `--timeout` CLI option are different, but work together to ensure that jobs are not lost and that jobs are only successfully processed once. > {note} The `--timeout` value should always be at least several seconds shorter than your `retry_after` configuration value. This will ensure that a worker processing a given job is always killed before the job is retried. If your `--timeout` option is longer than your `retry_after` configuration value, your jobs may be processed twice. +#### Worker Sleep Duration + +When jobs are available on the queue, the worker will keep processing jobs with no delay in between them. However, the `sleep` option determines how long the worker will "sleep" if there are no new jobs available: + + php artisan queue:work --sleep=3 + ## Supervisor Configuration @@ -394,6 +464,7 @@ You may define a `failed` method directly on your job class, allowing you to per use Exception; use App\Podcast; use App\AudioProcessor; + use Illuminate\Bus\Queueable; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; @@ -432,7 +503,7 @@ You may define a `failed` method directly on your job class, allowing you to per * @param Exception $exception * @return void */ - public function failed(Exception $e) + public function failed(Exception $exception) { // Send user notification of failure, etc... } @@ -547,3 +618,11 @@ Using the `before` and `after` methods on the `Queue` [facade](/docs/{{version}} // } } + +Using the `looping` method on the `Queue` [facade](/docs/{{version}}/facades), you may specify callbacks that execute before the worker attempts to fetch a job from a queue. For example, you might register a Closure to rollback any transactions that were left open by a previously failed job: + + Queue::looping(function () { + while (DB::transactionLevel() > 0) { + DB::rollBack(); + } + }); diff --git a/readme.md b/readme.md index 9b55bba..6539e3d 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,14 @@ -# Laravel Documentation +ရည်ရွယ်ချက် +--------- -## Contribution Guidelines +ကျွန်တော်တို့ အခုမှစပြီး ဘာသာပြန်နေတုံးပါ၊ ရည်ရွယ်ချက် ကတော့ Laravel PHP Framework Documencation ကို မြန်မာလို ဖတ်ပြီး Laravel Framework ကို အလွယ်တကူ လေ့လာနိုင်ဖို့ပါ၊ -If you are submitting documentation for the **current stable release**, submit it to the corresponding branch. For example, documentation for Laravel 5.1 would be submitted to the `5.1` branch. Documentation intended for the next release of Laravel should be submitted to the `master` branch. +ဒီဘာသာပြန်မှုမှာ ဘယ်လို ဘာဝင်ကူညီနိုင်လဲ +-------------------- + +ဟုတ်ကဲ့ ကျွန်တော်တို့လည်းအလုပ်နဲ့ပါ အားတဲ့အချိန်လေးဘာသာပြန်ပြီးတော့ဒီမှာတင်တာပါ။ တကယ်လို့ကျွန်တော်တို့ကို ကူညီပြီးဘာသာပြန်ချင်တယ်ဆိုရင်ဒီ Repo မှာ pull request လုပ်ပြီး contribute လုပ်နိုင်ပါတယ်။ ကျွန်တော်တို့ဘာသာပြန်ပြီးသားတွေကိုလည်း စာလုံးပေါင်းအမှားတွေကို git issue တွေပေးနိုင်ပါတယ်။ [Contribute Guide](contributing.md) ကိုဖတ်ပေးပါ။ + +ဆက်သွယ်ရန် +------- + +- [Laravel Myanmar Facebook Group](https://www.facebook.com/groups/250409601822202/) \ No newline at end of file diff --git a/redis.md b/redis.md index 7a52a36..b14b2e1 100644 --- a/redis.md +++ b/redis.md @@ -2,6 +2,8 @@ - [Introduction](#introduction) - [Configuration](#configuration) + - [Predis](#predis) + - [PhpRedis](#phpredis) - [Interacting With Redis](#interacting-with-redis) - [Pipelining Commands](#pipelining-commands) - [Pub / Sub](#pubsub) @@ -9,10 +11,14 @@ ## Introduction -[Redis](http://redis.io) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain [strings](http://redis.io/topics/data-types#strings), [hashes](http://redis.io/topics/data-types#hashes), [lists](http://redis.io/topics/data-types#lists), [sets](http://redis.io/topics/data-types#sets), and [sorted sets](http://redis.io/topics/data-types#sorted-sets). Before using Redis with Laravel, you will need to install the `predis/predis` package via Composer: +[Redis](http://redis.io) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain [strings](http://redis.io/topics/data-types#strings), [hashes](http://redis.io/topics/data-types#hashes), [lists](http://redis.io/topics/data-types#lists), [sets](http://redis.io/topics/data-types#sets), and [sorted sets](http://redis.io/topics/data-types#sorted-sets). + +Before using Redis with Laravel, you will need to install the `predis/predis` package via Composer: composer require predis/predis +Alternatively, you may install the [PhpRedis](https://github.com/phpredis/phpredis) PHP extension via PECL. The extension is more complex to install but may yield better performance for applications that make heavy use of Redis. + ### Configuration @@ -20,11 +26,12 @@ The Redis configuration for your application is located in the `config/database. 'redis' => [ - 'cluster' => false, + 'client' => 'predis', 'default' => [ - 'host' => '127.0.0.1', - 'port' => 6379, + 'host' => env('REDIS_HOST', 'localhost'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), 'database' => 0, ], @@ -32,13 +39,79 @@ The Redis configuration for your application is located in the `config/database. The default server configuration should suffice for development. However, you are free to modify this array based on your environment. Each Redis server defined in your configuration file is required to have a name, host, and port. -The `cluster` option will instruct the Laravel Redis client to perform client-side sharding across your Redis nodes, allowing you to pool nodes and create a large amount of available RAM. However, note that client-side sharding does not handle failover; therefore, is primarily suited for cached data that is available from another primary data store. +#### Configuring Clusters + +If your application is utilizing a cluster of Redis servers, you should define these clusters within a `clusters` key of your Redis configuration: + + 'redis' => [ + + 'client' => 'predis', + + 'clusters' => [ + 'default' => [ + [ + 'host' => env('REDIS_HOST', 'localhost'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => 0, + ], + ], + ], + + ], + +By default, clusters will perform client-side sharding across your nodes, allowing you to pool nodes and create a large amount of available RAM. However, note that client-side sharding does not handle failover; therefore, is primarily suited for cached data that is available from another primary data store. If you would like to use native Redis clustering, you should specify this in the `options` key of your Redis configuration: + + 'redis' => [ -Additionally, you may define an `options` array value in your Redis connection definition, allowing you to specify a set of Predis [client options](https://github.com/nrk/predis/wiki/Client-Options). + 'client' => 'predis', -If your Redis server requires authentication, you may supply a password by adding a `password` configuration item to your Redis server configuration array. + 'options' => [ + 'cluster' => 'redis', + ], + + 'clusters' => [ + // ... + ], -> {note} If you have the Redis PHP extension installed via PECL, you will need to rename the alias for Redis in your `config/app.php` file. + ], + + +### Predis + +In addition to the default `host`, `port`, `database`, and `password` server configuration options, Predis supports additional [connection parameters](https://github.com/nrk/predis/wiki/Connection-Parameters) that may be defined for each of your Redis servers. To utilize these additional configuration options, simply add them to your Redis server configuration in the `config/database.php` configuration file: + + 'default' => [ + 'host' => env('REDIS_HOST', 'localhost'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => 0, + 'read_write_timeout' => 60, + ], + + +### PhpRedis + +> {note} If you have the PhpRedis PHP extension installed via PECL, you will need to rename the `Redis` alias in your `config/app.php` configuration file. + +To utilize the PhpRedis extension, you should change the `client` option of your Redis configuration to `phpredis`. This option is found in your `config/database.php` configuration file: + + 'redis' => [ + + 'client' => 'phpredis', + + // Rest of Redis configuration... + ], + +In addition to the default `host`, `port`, `database`, and `password` server configuration options, PhpRedis supports the following additional connection parameters: `persistent`, `prefix`, `read_timeout` and `timeout`. You may add any of these options to your Redis server configuration in the `config/database.php` configuration file: + + 'default' => [ + 'host' => env('REDIS_HOST', 'localhost'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => 0, + 'read_timeout' => 60, + ], ## Interacting With Redis @@ -84,9 +157,9 @@ You may get a Redis instance by calling the `Redis::connection` method: $redis = Redis::connection(); -This will give you an instance of the default Redis server. If you are not using server clustering, you may pass the server name to the `connection` method to get a specific server as defined in your Redis configuration: +This will give you an instance of the default Redis server. You may also pass the connection or cluster name to the `connection` method to get a specific server or cluster as defined in your Redis configuration: - $redis = Redis::connection('other'); + $redis = Redis::connection('my-connection'); ### Pipelining Commands @@ -136,7 +209,7 @@ First, let's setup a channel listener using the `subscribe` method. We'll place */ public function handle() { - Redis::subscribe(['test-channel'], function($message) { + Redis::subscribe(['test-channel'], function ($message) { echo $message; }); } @@ -154,10 +227,10 @@ Now we may publish messages to the channel using the `publish` method: Using the `psubscribe` method, you may subscribe to a wildcard channel, which may be useful for catching all messages on all channels. The `$channel` name will be passed as the second argument to the provided callback `Closure`: - Redis::psubscribe(['*'], function($message, $channel) { + Redis::psubscribe(['*'], function ($message, $channel) { echo $message; }); - Redis::psubscribe(['users.*'], function($message, $channel) { + Redis::psubscribe(['users.*'], function ($message, $channel) { echo $message; }); diff --git a/releases.md b/releases.md index d31a832..fbecae7 100644 --- a/releases.md +++ b/releases.md @@ -1,6 +1,8 @@ # Release Notes - [Support Policy](#support-policy) +- [Laravel 5.4](#laravel-5.4) +- [Laravel 5.3](#laravel-5.3) - [Laravel 5.2](#laravel-5.2) - [Laravel 5.1.11](#laravel-5.1.11) - [Laravel 5.1.4](#laravel-5.1.4) @@ -12,9 +14,553 @@ ## Support Policy -For LTS releases, such as Laravel 5.1, bug fixes are provided for 2 years and security fixes are provided for 3 years. These releases provide the longest window of support and maintenance. +For LTS releases, such as Laravel 5.1, bug fixes are provided for 2 years and security fixes are provided for 3 years. These releases provide the longest window of support and maintenance. For general releases, bug fixes are provided for 6 months and security fixes are provided for 1 year. -For general releases, bug fixes are provided for 6 months and security fixes are provided for 1 year. + +## Laravel 5.4 + +Laravel 5.4 continues the improvements made in Laravel 5.3 by adding support for [Markdown based emails and notifications](/docs/5.4/mail#markdown-mailables), the [Laravel Dusk](/docs/5.4/dusk) browser automation and testing framework, Laravel Mix, Blade "components" and "slots", route model binding on broadcast channels, higher order messages for Collections, object-based Eloquent events, job-level "retry" and "timeout" settings, "realtime" facades, improved support for Redis Cluster, custom pivot table models, middleware for request input trimming and cleaning, and more. In addition, the entire codebase of the framework was reviewed and refactored for general cleanliness. + +> {tip} This documentation summarizes the most notable improvements to the framework; however, more thorough change logs are always available [on GitHub](https://github.com/laravel/framework/blob/5.4/CHANGELOG-5.4.md). + +### Markdown Mail & Notifications + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-4/episodes/7) for this feature available on Laracasts. + +Markdown mailable messages allow you to take advantage of the pre-built templates and components of mail notifications in your mailables. Since the messages are written in Markdown, Laravel is able to render beautiful, responsive HTML templates for the messages while also automatically generating a plain-text counterpart. For example, a Markdown email might look something like the following: + + @component('mail::message') + # Order Shipped + + Your order has been shipped! + + @component('mail::button', ['url' => $url]) + View Order + @endcomponent + + Next Steps: + + - Track Your Order On Our Website + - Pre-Sign For Delivery + + Thanks,
+ {{ config('app.name') }} + @endcomponent + +Using this simple Markdown template, Laravel is able to generate a responsive HTML email and plain-text counterpart: + + + +To read more about Markdown mail and notifications, check out the full [mail](/docs/5.4/mail) and [notification](/docs/5.4/notifications) documentation. + +> {tip} You may export all of the Markdown mail components to your own application for customization. To export the components, use the `vendor:publish` Artisan command to publish the `laravel-mail` asset tag. + +### Laravel Dusk + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-4/episodes/9) for this feature available on Laracasts. + +Laravel Dusk provides an expressive, easy-to-use browser automation and testing API. By default, Dusk does not require you to install JDK or Selenium on your machine. Instead, Dusk uses a standalone [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/home) installation. However, you are free to utilize any other Selenium compatible driver you wish. + +Since Dusk operates using a real browser, you are able to easily test and interact with your applications that heavily use JavaScript: + + /** + * A basic browser test example. + * + * @return void + */ + public function testBasicExample() + { + $user = factory(User::class)->create([ + 'email' => 'taylor@laravel.com', + ]); + + $this->browse(function ($browser) use ($user) { + $browser->loginAs($user) + ->visit('/home') + ->press('Create Playlist') + ->whenAvailable('.playlist-modal', function ($modal) { + $modal->type('name', 'My Playlist') + ->press('Create'); + }); + + $browser->waitForText('Playlist Created'); + }); + } + +For more information on Dusk, consult the full [Dusk documentation](/docs/5.4/dusk). + +### Laravel Mix + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-4/episodes/3) for this feature available on Laracasts. + +Laravel Mix is the spiritual successor of Laravel Elixir, and its entirely based on Webpack instead of Gulp. Laravel Mix provides a fluent API for defining Webpack build steps for your Laravel application using several common CSS and JavaScript pre-processors. Through simple method chaining, you can fluently define your asset pipeline. For example: + + mix.js('resources/assets/js/app.js', 'public/js') + .sass('resources/assets/sass/app.scss', 'public/css'); + +### Blade Components & Slots + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-4/episodes/6) for this feature available on Laracasts. + +Blade components and slots provide similar benefits to sections and layouts; however, some may find the mental model of components and slots easier to understand. First, let's imagine a reusable "alert" component we would like to reuse throughout our application: + + + +
+ {{ $slot }} +
+ +The `{{ $slot }}` variable will contain the content we wish to inject into the component. Now, to construct this component, we can use the `@component` Blade directive: + + @component('alert') + Whoops! Something went wrong! + @endcomponent + +Named slots allow you to provide multiple slots into a single component: + + + +
+
{{ $title }}
+ + {{ $slot }} +
+ +Named slots may be injected using the `@slot` directive. Any content is not within a `@slot` directive will be passed to the component in the `$slot` variable: + + @component('alert') + @slot('title') + Forbidden + @endslot + + You are not allowed to access this resource! + @endcomponent + +To read more about components and slots, consult the full [Blade documentation](/docs/5.4/blade). + +### Broadcast Model Binding + +Just like HTTP routes, channel routes may now take advantage of implicit and explicit [route model binding](/docs/5.4/routing#route-model-binding). For example, instead of receiving the string or numeric order ID, you may request an actual `Order` model instance: + + use App\Order; + + Broadcast::channel('order.{order}', function ($user, Order $order) { + return $user->id === $order->user_id; + }); + +To read more about broadcast model binding, consult the full [event broadcasting](/docs/5.4/broadcasting) documentation. + +### Collection Higher Order Messages + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-4/episodes/2) for this feature available on Laracasts. + +Collections now provide support for "higher order messages", which are short-cuts for performing common actions on collections. The collection methods that provide higher order messages are: `contains`, `each`, `every`, `filter`, `first`, `map`, `partition`, `reject`, `sortBy`, `sortByDesc`, and `sum`. + +Each higher order message can be accessed as a dynamic property on a collection instance. For instance, let's use the `each` higher order message to call a method on each object within a collection: + + $users = User::where('votes', '>', 500)->get(); + + $users->each->markAsVip(); + +Likewise, we can use the `sum` higher order message to gather the total number of "votes" for a collection of users: + + $users = User::where('group', 'Development')->get(); + + return $users->sum->votes; + +### Object Based Eloquent Events + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-4/episodes/10) for this feature available on Laracasts. + +Eloquent event handlers may now be mapped to event objects. This provides a more intuitive way of handling Eloquent events and makes it easier to test the events. To get started, define an `$events` property on your Eloquent model that maps various points of the Eloquent model's lifecycle to your own [event classes](/docs/5.4/events): + + UserSaved::class, + 'deleted' => UserDeleted::class, + ]; + } + +### Job Level Retry & Timeout + +Previously, queue job "retry" and "timeout" settings could only be configured globally for all jobs on the command line. However, in Laravel 5.4, these settings may be configured on a per-job basis by defining them directly on the job class: + + {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-4/episodes/1) for this feature available on Laracasts. + +Laravel 5.4 includes two new middleware in the default middleware stack: `TrimStrings` and `ConvertEmptyStringsToNull`: + + /** + * The application's global HTTP middleware stack. + * + * These middleware are run during every request to your application. + * + * @var array + */ + protected $middleware = [ + \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, + \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, + \App\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + ]; + +These middleware will automatically trim request input values and convert any empty strings to `null`. This helps you normalize the input for every request entering into your application and not have to worry about continually calling the `trim` function in every route and controller. + +### "Realtime" Facades + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-4/episodes/8) for this feature available on Laracasts. + +Previously, only Laravel's own built-in services exposed [facades](/docs/5.4/facades), which provide quick, terse access to their methods via the service container. However, in Laravel 5.4, you may easily convert any of your application's classes into a facade in realtime simply by prefixing the imported class name with `Facades`. For example, imagine your application contains a class like the following: + + tax = $tax; + } + + /** + * Pay the given amount. + * + * @param int $amount + * @return void + */ + public function pay($amount) + { + // Pay an amount... + } + } + +You may easily use this class as a facade like so: + + use Facades\ { + App\Services\PaymentGateway + }; + + Route::get('/pay/{amount}', function ($amount) { + PaymentGateway::pay($amount); + }); + +Of course, if you leverage a realtime facade in this way, you may easily write a test for the interaction using Laravel's [facade mocking capabilities](/docs/5.4/mocking): + + PaymentGateway::shouldReceive('pay')->with('100'); + +### Custom Pivot Table Models + +In Laravel 5.3, all "pivot" table models for `belongsToMany` relationships used the same built-in `Pivot` model instance. In Laravel 5.4, you may define custom models for your pivot tables. If you would like to define a custom model to represent the intermediate table of your relationship, use the `using` method when defining the relationship: + + belongsToMany('App\User')->using('App\UserRole'); + } + } + +### Improved Redis Cluster Support + +Previously, it was not possible to define Redis connections to single hosts and to clusters in the same application. In Laravel 5.4, you may now define Redis connections to multiple single hosts and multiple clusters within the same application. For more information on Redis in Laravel, please consult the full [Redis documentation](/docs/5.4/redis). + + +### Migration Default String Length + +Laravel 5.4 uses the `utf8mb4` character set by default, which includes support for storing "emojis" in the database. If you are upgrading your application from Laravel 5.3, you are not required to switch to this character set. + +If you choose to switch to this character set manually and are running a version of MySQL older than the 5.7.7 release, you may need to manually configure the default string length generated by migrations. You may configure this by calling the `Schema::defaultStringLength` method within your `AppServiceProvider`: + + use Illuminate\Support\Facades\Schema; + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + Schema::defaultStringLength(191); + } + + +## Laravel 5.3 + +Laravel 5.3 continues the improvements made in Laravel 5.2 by adding a driver based [notification system](/docs/5.3/notifications), robust realtime support via [Laravel Echo](/docs/5.3/broadcasting), painless OAuth2 servers via [Laravel Passport](/docs/5.3/passport), full-text model searching via [Laravel Scout](/docs/5.3/scout), Webpack support in Laravel Elixir, "mailable" objects, explicit separation of `web` and `api` routes, Closure based console commands, convenient helpers for storing uploaded files, support for POPO and single-action controllers, improved default frontend scaffolding, and more. + +### Notifications + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/9) for this feature available on Laracasts. + +Laravel Notifications provide a simple, expressive API for sending notifications across a variety of delivery channels such as email, Slack, SMS, and more. For example, you may define a notification that an invoice has been paid and deliver that notification via email and SMS. Then, you may send the notification using a single, simple method: + + $user->notify(new InvoicePaid($invoice)); + +There is already a wide variety of [community written drivers](http://laravel-notification-channels.com) for notifications, including support for iOS and Android notifications. To learn more about notifications, be sure to check out the [full notification documentation](/docs/5.3/notifications). + +### WebSockets / Event Broadcasting + +While event broadcasting existed in previous versions of Laravel, the Laravel 5.3 release greatly improves this feature of the framework by adding channel-level authentication for private and presence WebSocket channels: + + /* + * Authenticate the channel subscription... + */ + Broadcast::channel('orders.*', function ($user, $orderId) { + return $user->placedOrder($orderId); + }); + +Laravel Echo, a new JavaScript package installable via NPM, has also been released to provide a simple, beautiful API for subscribing to channels and listening for your server-side events in your client-side JavaScript application. Echo includes support for [Pusher](https://pusher.com) and [Socket.io](http://socket.io): + + Echo.channel('orders.' + orderId) + .listen('ShippingStatusUpdated', (e) => { + console.log(e.description); + }); + +In addition to subscribing to traditional channels, Laravel Echo also makes it a breeze to subscribe to presence channels which provide information about who is listening on a given channel: + + Echo.join('chat.' + roomId) + .here((users) => { + // + }) + .joining((user) => { + console.log(user.name); + }) + .leaving((user) => { + console.log(user.name); + }); + +To learn more about Echo and event broadcasting, check out the [full documentation](/docs/5.3/broadcasting). + +### Laravel Passport (OAuth2 Server) + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/13) for this feature available on Laracasts. + +Laravel 5.3 makes API authentication a breeze using [Laravel Passport](/docs/{{version}}/passport), which provides a full OAuth2 server implementation for your Laravel application in a matter of minutes. Passport is built on top of the [League OAuth2 server](https://github.com/thephpleague/oauth2-server) that is maintained by Alex Bilbie. + +Passport makes it painless to issue access tokens via OAuth2 authorization codes. You may also allow your users to create "personal access tokens" via your web UI. To get you started quickly, Passport includes [Vue components](https://vuejs.org) that can serve as a starting point for your OAuth2 dashboard, allowing users to create clients, revoke access tokens, and more: + + + + + +If you do not want to use the Vue components, you are welcome to provide your own frontend dashboard for managing clients and access tokens. Passport exposes a simple JSON API that you may use with any JavaScript framework you choose. + +Of course, Passport also makes it simple to define access token scopes that may be requested by application's consuming your API: + + Passport::tokensCan([ + 'place-orders' => 'Place new orders', + 'check-status' => 'Check order status', + ]); + +In addition, Passport includes helpful middleware for verifying that an access token authenticated request contains the necessary token scopes: + + Route::get('/orders/{order}/status', function (Order $order) { + // Access token has "check-status" scope... + })->middleware('scope:check-status'); + +Lastly, Passport includes support for consuming your own API from your JavaScript application without worrying about passing access tokens. Passport achieves this through encrypted JWT cookies and synchronized CSRF tokens, allowing you to focus on what matters: your application. For more information on Passport, be sure to check out its [full documentation](/docs/5.3/passport). + +### Search (Laravel Scout) + +Laravel Scout provides a simple, driver based solution for adding full-text search to your [Eloquent models](/docs/5.3/eloquent). Using model observers, Scout will automatically keep your search indexes in sync with your Eloquent records. Currently, Scout ships with an [Algolia](https://www.algolia.com/) driver; however, writing custom drivers is simple and you are free to extend Scout with your own search implementations. + +Making models searchable is as simple as adding a `Searchable` trait to the model: + + save(); + +Once your models have been indexed, its a breeze to perform full-text searches across all of your models. You may even paginate your search results: + + return Order::search('Star Trek')->get(); + + return Order::search('Star Trek')->where('user_id', 1)->paginate(); + +Of course, Scout has many more features which are covered in the [full documentation](/docs/5.3/scout). + +### Mailable Objects + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/6) for this feature available on Laracasts. + +Laravel 5.3 ships with support for mailable objects. These objects allow you to represent your email messages as a simple objects instead of customizing mail messages within Closures. For example, you may define a simple mailable object for a "welcome" email: + + class WelcomeMessage extends Mailable + { + use Queueable, SerializesModels; + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + return $this->view('emails.welcome'); + } + } + +Once the mailable object has been defined, you can send it to a user using a simple, expressive API. Mailable objects are great for discovering the intent of your messages while scanning your code: + + Mail::to($user)->send(new WelcomeMessage); + +Of course, you may also mark mailable objects as "queueable" so that they will be sent in the background by your queue workers: + + class WelcomeMessage extends Mailable implements ShouldQueue + { + // + } + +For more information on mailable objects, be sure to check out the [mail documentation](/docs/5.3/mail). + +### Storing Uploaded Files + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/12) for this feature available on Laracasts. + +In web applications, one of the most common use-cases for storing files is storing user uploaded files such as profile pictures, photos, and documents. Laravel 5.3 makes it very easy to store uploaded files using the new `store` method on an uploaded file instance. Simply call the `store` method with the path at which you wish to store the uploaded file: + + /** + * Update the avatar for the user. + * + * @param Request $request + * @return Response + */ + public function update(Request $request) + { + $path = $request->file('avatar')->store('avatars', 's3'); + + return $path; + } + +For more information on storing uploaded files, check out the [full documentation](/docs/{{version}}/filesystem#file-uploads). + + +### Webpack & Laravel Elixir + +Along with Laravel 5.3, Laravel Elixir 6.0 has been released with baked-in support for the Webpack and Rollup JavaScript module bundlers. By default, the Laravel 5.3 `gulpfile.js` file now uses Webpack to compile your JavaScript. The [full Laravel Elixir documentation](/docs/5.3/elixir) contains more information on both of these bundlers: + + elixir(mix => { + mix.sass('app.scss') + .webpack('app.js'); + }); + +### Frontend Structure + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/4) for this feature available on Laracasts. + +Laravel 5.3 ships with a more modern frontend structure. This primarily affects the `make:auth` authentication scaffolding. Instead of loading frontend assets from a CDN, dependencies are specified in the default `package.json` file. + +In addition, support for single file [Vue components](https://vuejs.org) is now included out of the box. A sample `Example.vue` component is included in the `resources/assets/js/components` directory. In addition, the new `resources/assets/js/app.js` file bootstraps and configures your JavaScript libraries and, if applicable, Vue components. + +This structure provides more guidance on how to begin developing modern, robust JavaScript applications, without requiring your application to use any given JavaScript or CSS framework. For more information on getting started with modern Laravel frontend development, check out the new [introductory frontend documentation](/docs/5.3/frontend). + +### Routes Files + +By default, fresh Laravel 5.3 applications contain two HTTP route files in a new top-level `routes` directory. The `web` and `api` route files provide more explicit guidance in how to split the routes for your web interface and your API. The routes in the `api` route file are automatically assigned the `api` prefix by the `RouteServiceProvider`. + +### Closure Console Commands + +In addition to being defined as command classes, Artisan commands may now be defined as simple Closures in the `commands` method of your `app/Console/Kernel.php` file. In fresh Laravel 5.3 applications, the `commands` method loads a `routes/console.php` file which allows you to define your Console commands as route-like, Closure based entry points into your application: + + Artisan::command('build {project}', function ($project) { + $this->info('Building project...'); + }); + +For more information on Closure commands, check out the [full Artisan documentation](/docs/5.3/artisan#closure-commands). + +### The `$loop` Variable + +> {video} There is a free [video tutorial](https://laracasts.com/series/whats-new-in-laravel-5-3/episodes/7) for this feature available on Laracasts. + +When looping within a Blade template, a `$loop` variable will be available inside of your loop. This variable provides access to some useful bits of information such as the current loop index and whether this is the first or last iteration through the loop: + + @foreach ($users as $user) + @if ($loop->first) + This is the first iteration. + @endif + + @if ($loop->last) + This is the last iteration. + @endif + +

This is user {{ $user->id }}

+ @endforeach + +For more information, consult the [full Blade documentation](/docs/5.3/blade#the-loop-variable). ## Laravel 5.2 @@ -157,7 +703,7 @@ Every page of the Laravel documentation has been meticulously reviewed and drama ### Event Broadcasting -In many modern web applications, web sockets are used to implement real-time, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a websocket connection to be handled by the client. +In many modern web applications, web sockets are used to implement realtime, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a websocket connection to be handled by the client. To assist you in building these types of applications, Laravel makes it easy to "broadcast" your events over a websocket connection. Broadcasting your Laravel events allows you to share the same event names between your server-side code and your client-side JavaScript framework. diff --git a/requests.md b/requests.md index 58102fd..fa70a40 100644 --- a/requests.md +++ b/requests.md @@ -244,7 +244,7 @@ You may attach a cookie to an outgoing `Illuminate\Http\Response` instance using 'name', 'value', $minutes ); -The `cookie` method also accepts a few more arguments which are used less frequently. Generally, these arguments have the same purpose and meaning as the arguments that would be given to PHP's native [setcookie](http://php.net/manual/en/function.setcookie.php) method: +The `cookie` method also accepts a few more arguments which are used less frequently. Generally, these arguments have the same purpose and meaning as the arguments that would be given to PHP's native [setcookie](https://secure.php.net/manual/en/function.setcookie.php) method: return response('Hello World')->cookie( 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly @@ -259,7 +259,7 @@ If you would like to generate a `Symfony\Component\HttpFoundation\Cookie` instan return response('Hello World')->cookie($cookie); -### Files +## Files ### Retrieving Uploaded Files @@ -301,7 +301,7 @@ There are a variety of other methods available on `UploadedFile` instances. Chec To store an uploaded file, you will typically use one of your configured [filesystems](/docs/{{version}}/filesystem). The `UploadedFile` class has a `store` method which will move an uploaded file to one of your disks, which may be a location on your local filesystem or even a cloud storage location like Amazon S3. -The `store` method accepts the path where the file should be stored relative to the filesystem's configured root directory. This path should not contain a file name, since the name will automatically be generated using the MD5 hash of the file's contents. +The `store` method accepts the path where the file should be stored relative to the filesystem's configured root directory. This path should not contain a file name, since a unique ID will automatically be generated to serve as the file name. The `store` method also accepts an optional second argument for the name of the disk that should be used to store the file. The method will return the path of the file relative to the disk's root: diff --git a/responses.md b/responses.md index e7d45b4..9acb20c 100644 --- a/responses.md +++ b/responses.md @@ -73,7 +73,7 @@ The `cookie` method on response instances allows you to easily attach cookies to ->header('Content-Type', $type) ->cookie('name', 'value', $minutes); -The `cookie` method also accepts a few more arguments which are used less frequently. Generally, these arguments have the same purpose and meaning as the arguments that would be given to PHP's native [setcookie](http://php.net/manual/en/function.setcookie.php) method: +The `cookie` method also accepts a few more arguments which are used less frequently. Generally, these arguments have the same purpose and meaning as the arguments that would be given to PHP's native [setcookie](https://secure.php.net/manual/en/function.setcookie.php) method: ->cookie($name, $value, $minutes, $path, $domain, $secure, $httpOnly) diff --git a/routing.md b/routing.md index 9e70e4b..2537788 100644 --- a/routing.md +++ b/routing.md @@ -4,6 +4,7 @@ - [Route Parameters](#route-parameters) - [Required Parameters](#required-parameters) - [Optional Parameters](#parameters-optional-parameters) + - [Regular Expression Constraints](#parameters-regular-expression-constraints) - [Named Routes](#named-routes) - [Route Groups](#route-groups) - [Middleware](#route-group-middleware) @@ -94,6 +95,46 @@ Occasionally you may need to specify a route parameter, but make the presence of return $name; }); + +### Regular Expression Constraints + +You may constrain the format of your route parameters using the `where` method on a route instance. The `where` method accepts the name of the parameter and a regular expression defining how the parameter should be constrained: + + Route::get('user/{name}', function ($name) { + // + })->where('name', '[A-Za-z]+'); + + Route::get('user/{id}', function ($id) { + // + })->where('id', '[0-9]+'); + + Route::get('user/{id}/{name}', function ($id, $name) { + // + })->where(['id' => '[0-9]+', 'name' => '[a-z]+']); + + +#### Global Constraints + +If you would like a route parameter to always be constrained by a given regular expression, you may use the `pattern` method. You should define these patterns in the `boot` method of your `RouteServiceProvider`: + + /** + * Define your route model bindings, pattern filters, etc. + * + * @return void + */ + public function boot() + { + Route::pattern('id', '[0-9]+'); + + parent::boot(); + } + +Once the pattern has been defined, it is automatically applied to all routes using that parameter name: + + Route::get('user/{id}', function ($id) { + // Only executed if {id} is numeric... + }); + ## Named Routes @@ -150,7 +191,7 @@ To assign middleware to all routes within a group, you may use the `middleware` Another common use-case for route groups is assigning the same PHP namespace to a group of controllers using the `namespace` parameter in the group array: - Route::group(['namespace' => 'Admin'], function() { + Route::group(['namespace' => 'Admin'], function () { // Controllers Within The "App\Http\Controllers\Admin" Namespace }); @@ -196,7 +237,7 @@ In this example, since the Eloquent `$user` variable defined on the route matche #### Customizing The Key Name -If you would like implicit model binding to use a database column other than `id` when retrieving a given model class, you may override the `getRouteKeyName` method on the Eloquent model: +If you would like model binding to use a database column other than `id` when retrieving a given model class, you may override the `getRouteKeyName` method on the Eloquent model: /** * Get the route key for the model. @@ -217,12 +258,12 @@ To register an explicit binding, use the router's `model` method to specify the { parent::boot(); - Route::model('user', 'App\User'); + Route::model('user', App\User::class); } Next, define a route that contains a `{user}` parameter: - $router->get('profile/{user}', function(App\User $user) { + Route::get('profile/{user}', function (App\User $user) { // }); @@ -234,9 +275,14 @@ If a matching model instance is not found in the database, a 404 HTTP response w If you wish to use your own resolution logic, you may use the `Route::bind` method. The `Closure` you pass to the `bind` method will receive the value of the URI segment and should return the instance of the class that should be injected into the route: - $router->bind('user', function ($value) { - return App\User::where('name', $value)->first(); - }); + public function boot() + { + parent::boot(); + + Route::bind('user', function ($value) { + return App\User::where('name', $value)->first(); + }); + } ## Form Method Spoofing @@ -263,4 +309,4 @@ You may use the `current`, `currentRouteName`, and `currentRouteAction` methods $action = Route::currentRouteAction(); -Refer to the API documentation for both the [underlying class of the Route facade](http://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) and [Route instance](http://laravel.com/api/{{version}}/Illuminate/Routing/Route.html) to review all accessible methods. +Refer to the API documentation for both the [underlying class of the Route facade](https://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) and [Route instance](https://laravel.com/api/{{version}}/Illuminate/Routing/Route.html) to review all accessible methods. diff --git a/scheduling.md b/scheduling.md index 0d74fa9..f280078 100644 --- a/scheduling.md +++ b/scheduling.md @@ -4,6 +4,7 @@ - [Defining Schedules](#defining-schedules) - [Schedule Frequency Options](#schedule-frequency-options) - [Preventing Task Overlaps](#preventing-task-overlaps) + - [Maintenance Mode](#maintenance-mode) - [Task Output](#task-output) - [Task Hooks](#task-hooks) @@ -60,10 +61,12 @@ You may define all of your scheduled tasks in the `schedule` method of the `App\ } } -In addition to scheduling `Closure` calls, you may also schedule [Artisan commands](/docs/{{version}}/artisan) and operating system commands. For example, you may use the `command` method to schedule an Artisan command: +In addition to scheduling `Closure` calls, you may also schedule [Artisan commands](/docs/{{version}}/artisan) and operating system commands. For example, you may use the `command` method to schedule an Artisan command using either the command's name or class: $schedule->command('emails:send --force')->daily(); + $schedule->command(EmailsCommand::class, ['--force'])->daily(); + The `exec` command may be used to issue a command to the operating system: $schedule->exec('node /home/forge/script.js')->daily(); @@ -81,6 +84,7 @@ Method | Description `->everyTenMinutes();` | Run the task every ten minutes `->everyThirtyMinutes();` | Run the task every thirty minutes `->hourly();` | Run the task every hour +`->hourlyAt(17);` | Run the task every hour at 17 mins past the hour `->daily();` | Run the task every day at midnight `->dailyAt('13:00');` | Run the task every day at 13:00 `->twiceDaily(1, 13);` | Run the task daily at 1:00 & 13:00 @@ -103,9 +107,7 @@ These methods may be combined with additional constraints to create even more fi ->weekdays() ->hourly() ->timezone('America/Chicago') - ->when(function () { - return date('H') >= 8 && date('H') <= 17; - }); + ->between('8:00', '17:00'); Below is a list of the additional schedule constraints: @@ -119,8 +121,23 @@ Method | Description `->thursdays();` | Limit the task to Thursday `->fridays();` | Limit the task to Friday `->saturdays();` | Limit the task to Saturday +`->between($start, $end);` | Limit the task to run between start and end times `->when(Closure);` | Limit the task based on a truth test +#### Between Time Constraints + +The `between` method may be used to limit the execution of a task based on the time of day: + + $schedule->command('reminders:send') + ->hourly() + ->between('7:00', '22:00'); + +Similarly, the `unlessBetween` method can be used to exclude the execution of a task for a period of time: + + $schedule->command('reminders:send') + ->hourly() + ->unlessBetween('23:00', '4:00'); + #### Truth Test Constraints The `when` method may be used to limit the execution of a task based on the result of a given truth test. In other words, if the given `Closure` returns `true`, the task will execute as long as no other constraining conditions prevent the task from running: @@ -146,6 +163,13 @@ By default, scheduled tasks will be run even if the previous instance of the tas In this example, the `emails:send` [Artisan command](/docs/{{version}}/artisan) will be run every minute if it is not already running. The `withoutOverlapping` method is especially useful if you have tasks that vary drastically in their execution time, preventing you from predicting exactly how long a given task will take. + +### Maintenance Mode + +Laravel's scheduled tasks will not run when Laravel is in [maintenance mode](/docs/{{version}}/configuration#maintenance-mode), since we don't want your tasks to interfere with any unfinished maintenance you may be performing on your server. However, if you would like to force a task to run even in maintenance mode, you may use the `evenInMaintenanceMode` method: + + $schedule->command('emails:send')->evenInMaintenanceMode(); + ## Task Output @@ -161,14 +185,14 @@ If you would like to append the output to a given file, you may use the `appendO ->daily() ->appendOutputTo($filePath); -Using the `emailOutputTo` method, you may e-mail the output to an e-mail address of your choice. Note that the output must first be sent to a file using the `sendOutputTo` method. Before e-mailing the output of a task, you should configure Laravel's [e-mail services](/docs/{{version}}/mail): +Using the `emailOutputTo` method, you may e-mail the output to an e-mail address of your choice. Before e-mailing the output of a task, you should configure Laravel's [e-mail services](/docs/{{version}}/mail): $schedule->command('foo') ->daily() ->sendOutputTo($filePath) ->emailOutputTo('foo@example.com'); -> {note} The `emailOutputTo` and `sendOutputTo` methods are exclusive to the `command` method and are not supported for `call`. +> {note} The `emailOutputTo`, `sendOutputTo` and `appendOutputTo` methods are exclusive to the `command` method and are not supported for `call`. ## Task Hooks diff --git a/scout.md b/scout.md index 380202f..915a1ed 100644 --- a/scout.md +++ b/scout.md @@ -16,6 +16,7 @@ - [Searching](#searching) - [Where Clauses](#where-clauses) - [Pagination](#pagination) +- [Custom Engines](#custom-engines) ## Introduction @@ -37,7 +38,7 @@ Next, you should add the `ScoutServiceProvider` to the `providers` array of your After registering the Scout service provider, you should publish the Scout configuration using the `vendor:publish` Artisan command. This command will publish the `scout.php` configuration file to your `config` directory: - php artisan vendor:publish + php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider" Finally, add the `Laravel\Scout\Searchable` trait to the model you would like to make searchable. This trait will register a model observer to keep the model in sync with your search driver: @@ -261,3 +262,42 @@ Once you have retrieved the results, you may display the results and render the {{ $orders->links() }} + + +## Custom Engines + +#### Writing The Engine + +If one of the built-in Scout search engines doesn't fit your needs, you may write your own custom engine and register it with Scout. Your engine should extend the `Laravel\Scout\Engines\Engine` abstract class. This abstract class contains five methods your custom engine must implement: + + use Laravel\Scout\Builder; + + abstract public function update($models); + abstract public function delete($models); + abstract public function search(Builder $builder); + abstract public function paginate(Builder $builder, $perPage, $page); + abstract public function map($results, $model); + +You may find it helpful to review the implementations of these methods on the `Laravel\Scout\Engines\AlgoliaEngine` class. This class will provide you with a good starting point for learning how to implement each of these methods in your own engine. + +#### Registering The Engine + +Once you have written your custom engine, you may register it with Scout using the `extend` method of the Scout engine manager. You should call the `extend` method from the `boot` method of your `AppServiceProvider` or any other service provider used by your application. For example, if you have written a `MySqlSearchEngine`, you may register it like so: + + use Laravel\Scout\EngineManager; + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + resolve(EngineManager::class)->extend('mysql', function () { + return new MySqlSearchEngine; + }); + } + +Once your engine has been registered, you may specify it as your default Scout `driver` in your `config/scout.php` configuration file: + + 'driver' => 'mysql', diff --git a/seeding.md b/seeding.md index 1ab6800..ddd9ed4 100644 --- a/seeding.md +++ b/seeding.md @@ -18,7 +18,7 @@ To generate a seeder, execute the `make:seeder` [Artisan command](/docs/{{versio php artisan make:seeder UsersTableSeeder -A seeder class only contains one method by default: `run`. This method is called when the `db:seed` [Artisan command](/docs/{{version}}/artisan) is executed. Within the `run` method, you may insert data into your database however you wish. You may use the [query builder](/docs/{{version}}/queries) to manually insert data or you may use [Eloquent model factories](/docs/{{version}}/database-testing#model-factories). +A seeder class only contains one method by default: `run`. This method is called when the `db:seed` [Artisan command](/docs/{{version}}/artisan) is executed. Within the `run` method, you may insert data into your database however you wish. You may use the [query builder](/docs/{{version}}/queries) to manually insert data or you may use [Eloquent model factories](/docs/{{version}}/database-testing#writing-factories). As an example, let's modify the default `DatabaseSeeder` class and add a database insert statement to the `run` method: @@ -47,7 +47,7 @@ As an example, let's modify the default `DatabaseSeeder` class and add a databas ### Using Model Factories -Of course, manually specifying the attributes for each model seed is cumbersome. Instead, you can use [model factories](/docs/{{version}}/database-testing#model-factories) to conveniently generate large amounts of database records. First, review the [model factory documentation](/docs/{{version}}/database-testing#model-factories) to learn how to define your factories. Once you have defined your factories, you may use the `factory` helper function to insert records into your database. +Of course, manually specifying the attributes for each model seed is cumbersome. Instead, you can use [model factories](/docs/{{version}}/database-testing#writing-factories) to conveniently generate large amounts of database records. First, review the [model factory documentation](/docs/{{version}}/database-testing#writing-factories) to learn how to define your factories. Once you have defined your factories, you may use the `factory` helper function to insert records into your database. For example, let's create 50 users and attach a relationship to each user: @@ -58,7 +58,7 @@ For example, let's create 50 users and attach a relationship to each user: */ public function run() { - factory(App\User::class, 50)->create()->each(function($u) { + factory(App\User::class, 50)->create()->each(function ($u) { $u->posts()->save(factory(App\Post::class)->make()); }); } diff --git a/session.md b/session.md index 1013cd5..346c2d7 100644 --- a/session.md +++ b/session.md @@ -16,7 +16,7 @@ ## Introduction -Since HTTP driven applications are stateless, sessions provide a way to store information about the user across multiple requests. Laravel ships with a variety of session backends that are accessed through an expressive, unified API. Support for popular backends such as [Memcached](http://memcached.org), [Redis](http://redis.io), and databases is included out of the box. +Since HTTP driven applications are stateless, sessions provide a way to store information about the user across multiple requests. Laravel ships with a variety of session backends that are accessed through an expressive, unified API. Support for popular backends such as [Memcached](https://memcached.org), [Redis](http://redis.io), and databases is included out of the box. ### Configuration @@ -59,7 +59,7 @@ You may use the `session:table` Artisan command to generate this migration: #### Redis -Before using Redis sessions with Laravel, you will need to install the `predis/predis` package (~1.0) via Composer. You may configure your Redis connections in the `database` configuration file. In the `session` configuration file, the `database` option may be used to specify which Redis connection is used by the session. +Before using Redis sessions with Laravel, you will need to install the `predis/predis` package (~1.0) via Composer. You may configure your Redis connections in the `database` configuration file. In the `session` configuration file, the `connection` option may be used to specify which Redis connection is used by the session. ## Using The Session @@ -97,7 +97,7 @@ When you retrieve a value from the session, you may also pass a default value as $value = $request->session()->get('key', 'default'); - $value = $request->session()->get('key', function() { + $value = $request->session()->get('key', function () { return 'default'; }); @@ -249,7 +249,7 @@ Once your driver has been implemented, you are ready to register it with the fra */ public function boot() { - Session::extend('mongo', function($app) { + Session::extend('mongo', function ($app) { // Return implementation of SessionHandlerInterface... return new MongoSessionStore; }); diff --git a/structure.md b/structure.md index 022c598..f9952c8 100644 --- a/structure.md +++ b/structure.md @@ -71,10 +71,14 @@ The `resources` directory contains your views as well as your raw, un-compiled a #### The Routes Directory -The `routes` directory contains all of the route definitions for your application. By default, two route files are included with Laravel: `web.php` and `api.php`. The `web.php` file contains routes that the `RouteServiceProvider` places in the `web` middleware group, which provides session state, CSRF protection, and cookie encryption. If your application does not offer a stateless, RESTful API, all of your routes will most likely be defined in the `web.php` file. +The `routes` directory contains all of the route definitions for your application. By default, three route files are included with Laravel: `web.php`, `api.php`, and `console.php`. + +The `web.php` file contains routes that the `RouteServiceProvider` places in the `web` middleware group, which provides session state, CSRF protection, and cookie encryption. If your application does not offer a stateless, RESTful API, all of your routes will most likely be defined in the `web.php` file. The `api.php` file contains routes that the `RouteServiceProvider` places in the `api` middleware group, which provides rate limiting. These routes are intended to be stateless, so requests entering the application through these routes are intended to be authenticated via tokens and will not have access to session state. +The `console.php` file is where you may define all of your Closure based console commands. Each Closure is bound to a command instance allowing a simple approach to interacting with each command's IO methods. Even though this file does not define HTTP routes, it defines console based entry points (routes) into your application. + #### The Storage Directory @@ -106,12 +110,12 @@ A variety of other directories will be generated inside the `app` directory as y #### The Console Directory -The `Console` directory contains all of the custom Artisan commands for your application. These commands may be generate using the `make:command` command. This directory also houses your console kernel, which is where your custom Artisan commands are registered and your [scheduled tasks](/docs/{{version}}/scheduling) are defined. +The `Console` directory contains all of the custom Artisan commands for your application. These commands may be generated using the `make:command` command. This directory also houses your console kernel, which is where your custom Artisan commands are registered and your [scheduled tasks](/docs/{{version}}/scheduling) are defined. #### The Events Directory -This directory does not exist by default, but will be created for you by the `event:generate` and `event:make` Artisan commands. The `Events` directory, as you might expect, houses [event classes](/docs/{{version}}/events). Events may be used to alert other parts of your application that a given action has occurred, providing a great deal of flexibility and decoupling. +This directory does not exist by default, but will be created for you by the `event:generate` and `make:event` Artisan commands. The `Events` directory, as you might expect, houses [event classes](/docs/{{version}}/events). Events may be used to alert other parts of your application that a given action has occurred, providing a great deal of flexibility and decoupling. #### The Exceptions Directory diff --git a/summary.md b/summary.md new file mode 100644 index 0000000..751ad31 --- /dev/null +++ b/summary.md @@ -0,0 +1,8 @@ +# Summary + +* [Introduction](readme.md) +* Getting Started + * [Installation](installation.md) + * [Configuration](configuration.md) + * [Directory Structure](structure.md) + * [Errors & Logging](errors.md) \ No newline at end of file diff --git a/testing.md b/testing.md index 9d7141b..7a6abc4 100644 --- a/testing.md +++ b/testing.md @@ -1,4 +1,4 @@ -# Testing +# Testing: Getting Started - [Introduction](#introduction) - [Environment](#environment) @@ -9,12 +9,14 @@ Laravel is built with testing in mind. In fact, support for testing with PHPUnit is included out of the box and a `phpunit.xml` file is already setup for your application. The framework also ships with convenient helper methods that allow you to expressively test your applications. -An `ExampleTest.php` file is provided in the `tests` directory. After installing a new Laravel application, simply run `phpunit` on the command line to run your tests. +By default, your application's `tests` directory contains two directories: `Feature` and `Unit`. Unit tests are tests that focus on a very small, isolated portion of your code. In fact, most unit tests probably focus on a single method. Feature tests may test a larger portion of your code, including how several objects interact with each other or even a full HTTP request to a JSON endpoint. + +An `ExampleTest.php` file is provided in both the `Feature` and `Unit` test directories. After installing a new Laravel application, simply run `phpunit` on the command line to run your tests. ## Environment -When running tests, Laravel will automatically set the configuration environment to `testing`. Laravel automatically configures the session and cache to the `array` driver while testing, meaning no session or cache data will be persisted while testing. +When running tests via `phpunit`, Laravel will automatically set the configuration environment to `testing` because of the environment variables defined in the `phpunit.xml` file. Laravel also automatically configures the session and cache to the `array` driver while testing, meaning no session or cache data will be persisted while testing. You are free to define other testing environment configuration values as necessary. The `testing` environment variables may be configured in the `phpunit.xml` file, but make sure to clear your configuration cache using the `config:clear` Artisan command before running your tests! @@ -23,24 +25,30 @@ You are free to define other testing environment configuration values as necessa To create a new test case, use the `make:test` Artisan command: + // Create a test in the Feature directory... php artisan make:test UserTest -This command will place a new `UserTest` class within your `tests` directory. You may then define test methods as you normally would using PHPUnit. To run your tests, simply execute the `phpunit` command from your terminal: + // Create a test in the Unit directory... + php artisan make:test UserTest --unit + +Once the test has been generated, you may define test methods as you normally would using PHPUnit. To run your tests, simply execute the `phpunit` command from your terminal: assertTrue(true); } diff --git a/upgrade.md b/upgrade.md index 28300a1..f8dd13a 100644 --- a/upgrade.md +++ b/upgrade.md @@ -1,1265 +1,462 @@ # Upgrade Guide -- [Upgrading To 5.3.0 From 5.2](#upgrade-5.3.0) -- [Upgrading To 5.2.0 From 5.1](#upgrade-5.2.0) -- [Upgrading To 5.1.11](#upgrade-5.1.11) -- [Upgrading To 5.1.0](#upgrade-5.1.0) -- [Upgrading To 5.0.16](#upgrade-5.0.16) -- [Upgrading To 5.0 From 4.2](#upgrade-5.0) -- [Upgrading To 4.2 From 4.1](#upgrade-4.2) -- [Upgrading To 4.1.29 From <= 4.1.x](#upgrade-4.1.29) -- [Upgrading To 4.1.26 From <= 4.1.25](#upgrade-4.1.26) -- [Upgrading To 4.1 From 4.0](#upgrade-4.1) - - -## Upgrading To 5.3.0 From 5.2 - -#### Estimated Upgrade Time: 2-3 Hours +- [Upgrading To 5.4.0 From 5.3](#upgrade-5.4.0) -> {note} We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. - -### PHP & HHVM - -Laravel 5.3 requires PHP 5.6.4 or higher. HHVM is no longer officially supported as it does not contain the same language features as PHP 5.6+. - -### Deprecations - -All of the deprecations listed in the [Laravel 5.2 upgrade guide](#5.2-deprecations) have been removed from the framework. You should review this list to verify you are no longer using these deprecated features. - -### Arrays - -#### Key / Value Order Change + +## Upgrading To 5.4.0 From 5.3 -The `first`, `last`, and `contains` methods on the `Arr` class now pass the "value" as the first parameter to the given callback Closure. For example: +#### Estimated Upgrade Time: 1-2 Hours - Arr::first(function ($value, $key) { - return ! is_null($value); - }); - -In previous versions of Laravel, the `$key` was passed first. Since most use cases are only interested in the `$value` it is now passed first. You should do a "global find" in your application for these methods to verify that you are expecting the `$value` to be passed as the first argument to your Closure. - -### Artisan - -##### The `make:console` Command +> {note} We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. -The `make:console` command has been renamed to `make:command`. +### Updating Dependencies -### Authentication +Update your `laravel/framework` dependency to `5.4.*` in your `composer.json` file. In addition, you should update your `phpunit/phpunit` dependency to `~5.0`. -#### Authentication Scaffolding +#### Flushing The Cache -The two default authentication controllers provided with the framework have been split into four smaller controllers. This change provides cleaner, more focused authentication controllers by default. The easiest way to upgrade your application to the new authentication controllers is to [grab a fresh copy of each controller from GitHub](https://github.com/laravel/laravel/tree/master/app/Http/Controllers/Auth) and place them into your application. +After upgrading all packages, you should run `php artisan view:clear` to avoid Blade errors related to the removal of `Illuminate\View\Factory::getFirstLoop()`. In addition, you may need to run `php artisan route:clear` to flush the route cache. -You should also make sure that you are calling the `Route::auth()` method in your `routes.php` file. This method will register the proper routes for the new authentication controllers. +#### Laravel Cashier -Once these controllers have been placed into your application, you may need to re-implement any customizations you made to these controllers. For example, if you are customizing the authentication guard that is used for authentication, you may need to override the controller's `guard` method. You can examine each authentication controller's trait to determine which methods to override. +Laravel Cashier is already compatible with Laravel 5.4. -> {tip} If you were not customizing the authentication controllers, you should just be able to drop in fresh copies of the controllers from GitHub and verify that you are calling the `Route::auth` method in your `routes.php` file. +#### Laravel Passport -#### Password Reset Emails +Laravel Passport `2.0.0` has been released to provide compatibility with Laravel 5.4 and the [Axios](https://github.com/mzabriskie/axios) JavaScript library. If you are upgrading from Laravel 5.3 and using the pre-built Passport Vue components, you should make sure the Axios library is globally available to your application as `axios`. -Password reset emails now use the new Laravel notifications feature. If you would like to customize the notification sent when sending password reset links, you should override the `sendPasswordResetNotification` method of the `Illuminate\Auth\Passwords\CanResetPassword` trait. +#### Laravel Scout -Your `User` model **must** use the new `Illuminate\Notifications\Notifiable` trait in order for password reset link emails to be delivered: +Laravel Scout `3.0.0` has been released to provide compatibility with Laravel 5.4. - {note} Don't forget to register the `Illuminate\Notifications\NotificationServiceProvider` in the `providers` array of your `config/app.php` configuration file. + composer require laravel/tinker -#### POST To Logout +Once the package has been installed, you should add `Laravel\Tinker\TinkerServiceProvider::class` to the `providers` array in your `config/app.php` configuration file. -The `Route::auth` method now registers a `POST` route for `/logout` instead of a `GET` route. This prevents other web applications from logging your users out of your application. To upgrade, you should either convert your logout requests to use the `POST` verb or register your own `GET` route for the `/logout` URI: +#### Guzzle - Route::get('/logout', 'Auth\LoginController@logout'); +Laravel 5.4 requires Guzzle 6.0 or greater. ### Authorization -#### Calling Policy Methods With Class Names - -Some policy methods only receive the currently authenticated user and not an instance of the model they authorize. This situation is most common when authorizing `create` actions. For example, if you are creating a blog, you may wish to check if a user is authorized to create any posts at all. - -When defining policy methods that will not receive a model instance, such as a `create` method, the class name will no longer be passed as the second argument to the method. Your method should just expect the authenticated user instance: - - /** - * Determine if the given user can create posts. - * - * @param \App\User $user - * @return bool - */ - public function create(User $user) - { - // - } - -#### The `AuthorizesResources` Trait - -The `AuthorizesResources` trait has been merged with the `AuthorizesRequests` trait. You should remove the `AuthorizesResources` trait from your `app/Http/Controllers/Controller.php` file. - -### Blade - -#### Custom Directives - -In prior versions of Laravel, when registering custom Blade directives using the `directive` method, the `$expression` passed to your directive callback contained the outer-most parenthesis. In Laravel 5.3, these outer-most parenthesis are not included in the expression passed to your directive callback. Be sure to review the [Blade extension](/docs/5.3/blade#extending-blade) documentation and verify your custom Blade directives are still working properly. - -### Broadcasting - -#### Service Provider - -Laravel 5.3 includes significant improvements to [event broadcasting](/docs/{{version}}/broadcasting). You should add the new `BroadcastServiceProvider` to your `app/Providers` directory by [grabbing a fresh copy of the source from GitHub](https://raw.githubusercontent.com/laravel/laravel/develop/app/Providers/BroadcastServiceProvider.php). Once you have defined the new service provider, you should add it to the `providers` array of your `config/app.php` configuration file. - -### Cache - -#### Extension Closure Binding & `$this` - -When calling the `Cache::extend` method with a Closure, `$this` will be bound to the `CacheManager` instance, allowing you to call its methods from within your extension Closure: - - Cache::extend('memcached', function ($app, $config) { - try { - return $this->createMemcachedDriver($config); - } catch (Exception $e) { - return $this->createNullDriver($config); - } - }); - -### Collections - -#### Key / Value Order Change - -The `first`, `last`, and `contains` collection methods all pass the "value" as the first parameter to their given callback Closure. For example: - - $collection->first(function ($value, $key) { - return ! is_null($value); - }); - -In previous versions of Laravel, the `$key` was passed first. Since most use cases are only interested in the `$value` it is now passed first. You should do a "global find" in your application for these methods to verify that you are expecting the `$value` to be passed as the first argument to your Closure. - -#### `where` Comparison Now "Loose" By Default - -The `where` method now performs a "loose" comparison by default instead of a strict comparison. If you would like to perform a strict comparison, you may use the `whereStrict` method. - -The `where` method also no longer accepts a third parameter to indicate "strictness". You should explicit call either `where` or `whereStrict` depending on your application's needs. - -### Database - -#### Collections - -The [fluent query builder](/docs/{{version}}/queries) now returns `Illuminate\Support\Collection` instances instead of plain arrays. This brings consistency to the result types returned by the fluent query builder and Eloquent. - -If you do not want to migrate your query builder results to `Collection` instances, you may chain the `all` method onto your calls to the query builder's `get` method. This will return a plain PHP array of the results, allowing you to maintain backwards compatibility: - - $users = DB::table('users')->get()->all(); - -#### Eloquent `$morphClass` Property +#### The `getPolicyFor` Method -The `$morphClass` property that could be defined on Eloquent models has been removed in favor of defining a "morph map". Defining a morph map provides support for eager loading and resolves additional bugs with polymorphic relations. If you were previously relying on the `$morphClass` property, you should migrate to `morphMap` using the following syntax: +Previous, when calling the `Gate::getPolicyFor($class)` method, an exception was thrown if no policy could be found. Now, the method will return `null` if no policy is found for the given class. If you call this method directly, make sure you refactor your code to check for `null`: ```php -Relation::morphMap([ - 'YourCustomMorphName' => YourModel::class, -]); -``` - -For example, if you previously defined the following `$morphClass`: +$policy = Gate::getPolicyFor($class); -```php -class User extends Model -{ - protected $morphClass = 'user' +if ($policy) { + // code that was previously in the try block +} else { + // code that was previously in the catch block } ``` -You should define the following `morphMap` in the `boot` method of your `AppServiceProvider`: - -```php -use Illuminate\Database\Eloquent\Relation; - -Relation::morphMap([ - 'user' => User::class, -]); -``` +### Blade -#### Eloquent `save` Method +#### `@section` Escaping -The Eloquent `save` method now returns `false` if the model has not been changed since the last time it was retrieved or saved. +In Laravel 5.4, inline content passed to a section is automatically escaped: -#### Eloquent Scopes + @section('title', $content) -Eloquent scopes now respect the leading boolean of scope constraints. For example, if you are starting your scope with an `orWhere` constraint it will no longer be converted to normal `where`. If you were relying on this feature (e.g. adding multiple `orWhere` constraints within a loop), you should verify that the first condition is a normal `where` to avoid any boolean logic issues. +If you would like to render unescaped content in a section, you must declare the section using the traditional "long form" style: -If your scopes begin with `where` constraints no action is required. Remember, you can verify your query SQL using the `toSql` method of a query: + @section('title') + {!! $content !!} + @stop - User::where('foo', 'bar')->toSql(); +### Bootstrappers -#### Join Clause +If you are manually overriding the `$bootstrappers` array on your HTTP or Console kernel, you should rename the `DetectEnvironment` entry to `LoadEnvironmentVariables`. -The `JoinClause` class has been rewritten to unify its syntax with the query builder. The optional `$where` parameter of the `on` clause has been removed. To add a "where" conditions you should explicitly use one of the `where` methods offered by the [query builder](/docs/{{version}}/queries#where-clauses): +### Broadcasting - $query->join('table', function($join) { - $join->on('foo', 'bar')->where('bar', 'baz'); - }); +#### Channel Model Binding -The `$bindings` property was also removed. To manipulate join bindings directly you may use the `addBinding` method: +When defining channel name placeholders in Laravel 5.3, the `*` character is used. In Laravel 5.4, you should define these placeholders using `{foo}` style placeholders, like routes: - $query->join(DB::raw('('.$subquery->toSql().') table'), function($join) use ($subquery) { - $join->addBinding($subquery->getBindings(), 'join'); + Broadcast::channel('App.User.{userId}', function ($user, $userId) { + return (int) $user->id === (int) $userId; }); -### Encryption - -#### Mcrypt Encrypter Has Been Removed - -The Mcrypt encrypter was deprecated during the Laravel 5.1.0 release in June 2015. This encrypter has been totally removed in the 5.3.0 release in favor of the newer encryption implementation based on OpenSSL, which has been the default encryption scheme for all releases since Laravel 5.1.0. - -If you are still using an Mcrypt based `cipher` in your `config/app.php` configuration file, you should update the cipher to `AES-256-CBC` and set your key to a random 32 byte string which may be securely generated using `php artisan key:generate`. - -If you are storing encrypted data in your database using the Mcrypt encrypter, you may install the `laravel/legacy-encrypter` [package](https://github.com/laravel/legacy-encrypter) which includes the legacy Mcrypt encrypter implementation. You should use this package to decrypt your encrypted data and re-encrypt it using the new OpenSSL encrypter. For example, you may do something like the following in a [custom Artisan command](/docs/{{version}}/artisan): - - $legacy = new McryptEncrypter($encryptionKey); - - foreach ($records as $record) { - $record->encrypted = encrypt( - $legacy->decrypt($record->encrypted) - ); - - $record->save(); - } - -### Exception Handler - -#### Constructor - -The base exception handler class now requires a `Illuminate\Container\Container` instance to be passed to its constructor. This change will only affect your application if you have defined a custom `__construct` method in your `app/Exception/Handler.php` file. If you have done this, you should pass a container instance into the `parent::__construct` method: - - parent::__construct(app()); - -### Middleware - -#### `can` Middleware Namespace Change - -The `can` middleware listed in the `$routeMiddleware` property of your HTTP kernel should be updated to the following class: - - 'can' => \Illuminate\Auth\Middleware\Authorize::class, - -#### `can` Middleware Authentication Exception - -The `can` middleware will now throw an instance of `Illuminate\Auth\AuthenticationException` if the user is not authenticated. If you were manually catching a different exception type, you should update your application to catch this exception. In most cases, this change will not affect your application. - -#### Binding Substitution Middleware - -Route model binding is now accomplished using middleware. All applications should add the `Illuminate\Routing\Middleware\SubstituteBindings` to your `web` middleware group in your `app/Http/Kernel.php` file: - - \Illuminate\Routing\Middleware\SubstituteBindings::class, - -You should also register a route middleware for binding substitution in the `$routeMiddleware` property of your HTTP kernel: - - 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, - -Once this route middleware has been registered, you should add it to the `api` middleware group: - - 'api' => [ - 'throttle:60,1', - 'bindings', - ], - -### Notifications - -#### Installation - -Laravel 5.3 includes a new, driver based notification system. You should register the `Illuminate\Notifications\NotificationServiceProvider` in the `providers` array of your `config/app.php` configuration file. - -You should also add the `Illuminate\Support\Facades\Notification` facade to the `aliases` array of your `config/app.php` configuration file. - -Finally, you may use the `Illuminate\Notifications\Notifiable` trait on your `User` model or any other model you wish to receive notifications. - -### Pagination - -#### Customization - -Customizing the paginator's generated HTML is much easier in Laravel 5.3 compared to previous Laravel 5.x releases. Instead of defining a "Presenter" class, you only need to define a simple Blade template. The easiest way to customize the pagination views is by exporting them to your `resources/views/vendor` directory using the `vendor:publish` command: - - php artisan vendor:publish --tag=laravel-pagination - -This command will place the views in the `resources/views/vendor/pagination` directory. The `default.blade.php` file within this directory corresponds to the default pagination view. Simply edit this file to modify the pagination HTML. - -Be sure to review the full [pagination documentation](/docs/{{version}}/pagination) for more information. - -### Queue - -#### Configuration - -In your queue configuration, all `expire` configuration items should be renamed to `retry_after`. Likewise, the Beanstalk configuration's `ttr` item should be renamed to `retry_after`. This name change provides more clarity on the purpose of this configuration option. - -#### Closures - -Queueing closures is no longer supported. If you are queueing a Closure in your application, you should convert the Closure to a class and queue an instance of the class instead. - -#### Collection Serialization - -The `Illuminate\Queue\SerializesModels` trait now properly serializes instances of `Illuminate\Database\Eloquent\Collection`. This will most likely not be a breaking change for the vast majority of applications; however, if your application is absolutely dependent on collections not being re-retrieved from the database by queued jobs, you should verify that this change does not negatively affect your application. - -#### Daemon Workers - -It is no longer necessary to specify the `--daemon` option when calling the `queue:work` Artisan command. Running the `php artisan queue:work` command will automatically assume that you want to run the worker in daemon mode. If you would like to process a single job, you may use the `--once` option on the command: - - // Start a daemon queue worker... - php artisan queue:work - - // Process a single job... - php artisan queue:work --once - -#### Event Data Changes - -Various queue job events such as `JobProcessing` and `JobProcessed` no longer contain the `$data` property. You should update your application to call `$event->job->payload()` to get the equivalent data. - -#### Failed Jobs Table - -If your application has a `failed_jobs` table, you should add a `exception` column to the table. The `exception` column should be a `TEXT` type column and will be used to store a string representation of the exception that caused the job to fail. - -#### Serializing Models On Legacy Style Queue Jobs - -Typically, jobs in Laravel are queued by passing a new job instance to the `Queue::push` method. However, some applications may be queuing jobs using the following legacy syntax: - - Queue::push('ClassName@method'); - -If you are queueing jobs using this syntax, Eloquent models will no longer be automatically serialized and re-retrieved by the queue. If you would like your Eloquent models to be automatically serialized by the queue, you should use the `Illuminate\Queue\SerializesModels` trait on your job class and queue the job using the new `push` syntax: - - Queue::push(new ClassName); - -### Routing - -#### Resource Parameters Are Singular By Default - -In previous versions of Laravel, route parameters registered using `Route::resource` were not "singularized". This could lead to some unexpected behavior when registering route model bindings. For example, given the following `Route::resource` call: - - Route::resource('photos', 'PhotoController'); - -The URI for the `show` route would be defined as follows: - - /photos/{photos} - -In Laravel 5.3, all resource route parameters are singularized by default. So, the same call to `Route::resource` would register the following URI: - - /photos/{photo} - -If you would like to maintain the previous behavior instead of automatically singularizing resource route parameters, you may make the following call to the `singularResourceParameters` method in your `AppServiceProvider`: - - use Illuminate\Support\Facades\Route; - - Route::singularResourceParameters(false); - -#### Resource Route Names No Longer Affected By Prefixes - -URL prefixes no longer affect the route names assigned to routes when using `Route::resource`, since this behavior defeated the entire purpose of using route names in the first place. - -If your application is using `Route::resource` within a `Route::group` call that specified a `prefix` option, you should examine all of your calls to the `route` helper and verify that you are no longer appending the URI `prefix` to the route name. - -If this change causes you to have two routes with the same name, you may use the `names` option when calling `Route::resource` to specify a custom name for a given route. Refer to the [resource routing documentation](/docs/5.3/controllers#resource-controllers) for more information. - -### Validation - -#### Form Request Exceptions - -If a form request's validation fails, Laravel will now throw an instance of `Illuminate\Validation\ValidationException` instead of an instance of `HttpException`. If you are manually catching the `HttpException` instance thrown by a form request, you should update your `catch` blocks to catch the `ValidationException` instead. - -#### Nullable Primitives - -When validating arrays, booleans, integers, numerics, and strings, `null` will no longer be considered a valid value unless the rule set contains the new `nullable` rule: - - Validate::make($request->all(), [ - 'string' => 'nullable|max:5', - ]); - - -## Upgrading To 5.2.0 From 5.1 - -#### Estimated Upgrade Time: Less Than 1 Hour - -> {note} We attempt to provide a very comprehensive listing of every possible breaking change made to the framework. However, many of these changes may not apply to your own application. - -### Updating Dependencies - -Update your `composer.json` file to point to `laravel/framework 5.2.*`. - -Add `"symfony/dom-crawler": "~3.0"` and `"symfony/css-selector": "~3.0"` to the `require-dev` section of your `composer.json` file. - -### Authentication - -#### Configuration File - -You should update your `config/auth.php` configuration file with the following: [https://github.com/laravel/laravel/blob/master/config/auth.php](https://github.com/laravel/laravel/blob/master/config/auth.php) - -Once you have updated the file with a fresh copy, set your authentication configuration options to their desired value based on your old configuration file. If you were using the typical, Eloquent based authentication services available in Laravel 5.1, most values should remain the same. - -Take special note of the `passwords.users.email` configuration option in the new `auth.php` configuration file and verify that the view path matches the actual view path for your application, as the default path to this view was changed in Laravel 5.2. If the default value in the new configuration file does not match your existing view, update the configuration option. - -#### Contracts - -If you are implementing the `Illuminate\Contracts\Auth\Authenticatable` contract but are **not** using the `Authenticatable` trait, you should add a new `getAuthIdentifierName` method to your contract implementation. Typically, this method will return the column name of the "primary key" of your authenticatable entity. For example: `id`. - -This is unlikely to affect your application unless you were manually implementing this interface. - -#### Custom Drivers - -If you are using the `Auth::extend` method to define a custom method of retrieving users, you should now use `Auth::provider` to define your custom user provider. Once you have defined the custom provider, you may configure it in the `providers` array of your new `auth.php` configuration file. - -For more information on custom authentication providers, consult the [full authentication documentation](/docs/{{version}}/authentication). - -#### Redirection - -The `loginPath()` method has been removed from `Illuminate\Foundation\Auth\AuthenticatesUsers`, so placing a `$loginPath` variable in your `AuthController` is no longer required. By default, the trait will always redirect users back to their previous location on authentication errors. - -### Authorization - -The `Illuminate\Auth\Access\UnauthorizedException` has been renamed to `Illuminate\Auth\Access\AuthorizationException`. This is unlikely to affect your application if you are not manually catching this exception. - ### Collections -#### Eloquent Base Collections - -The Eloquent collection instance now returns a base Collection (`Illuminate\Support\Collection`) for the following methods: `pluck`, `keys`, `zip`, `collapse`, `flatten`, `flip`. +#### The `every` Method -#### Key Preservation +The behavior of the `every` method has been moved to the `nth` method to match the method name defined by Lodash. -The `slice`, `chunk`, and `reverse` methods now preserve keys on the collection. If you do not want these methods to preserve keys, use the `values` method on the `Collection` instance. +#### The `random` Method -### Composer Class +Calling `$collection->random(1)` will now return a new collection instance with one item. Previously, this would return a single object. This method will only return a single object if no arguments are supplied. -The `Illuminate\Foundation\Composer` class has been moved to `Illuminate\Support\Composer`. This is unlikely to affect your application if you were not manually using this class. +### Container -### Commands And Handlers +#### Binding Classes With Leading Slashes -#### Self-Handling Commands +Binding classes into the container with leading slashes is no longer supported. This feature required a significant amount of string formatting calls to be made within the container. Instead, simply register your bindings without a leading slash: -You no longer need to implement the `SelfHandling` contract on your jobs / commands. All jobs are now self-handling by default, so you can remove this interface from your classes. - -#### Separate Commands & Handlers - -The Laravel 5.2 command bus now only supports self-handling commands and no longer supports separate commands and handlers. - -If you would like to continue using separate commands and handlers, you may install a Laravel Collective package which provides backwards-compatible support for this: [https://github.com/LaravelCollective/bus](https://github.com/laravelcollective/bus) + $container->bind('Class\Name', function () { + // + }); -### Configuration + $container->bind(ClassName::class, function () { + // + }); -#### Environment Value +#### `make` Method Parameters -Add an `env` configuration option to your `app.php` configuration file that looks like the following: +The container's `make` method no longer accepts a second array of parameters. This feature typically indicates a code smell. Typically, you can always construct the object in another way that is more intuitive. - 'env' => env('APP_ENV', 'production'), +#### Resolving Callbacks -#### Caching And Env +The container's `resolving` and `afterResolving` method now must be provided a class name or binding key as the first argument to the method: -If you are using the `config:cache` command during deployment, you **must** make sure that you are only calling the `env` function from within your configuration files, and not from anywhere else in your application. + $container->resolving('Class\Name', function ($instance) { + // + }); -If you are calling `env` from within your application, it is strongly recommended you add proper configuration values to your configuration files and call `env` from that location instead, allowing you to convert your `env` calls to `config` calls. + $container->afterResolving('Class\Name', function ($instance) { + // + }); -#### Compiled Classes +#### `share` Method Removed -If present, remove the following lines from `config/compile.php` in the `files` array: +The `share` method has been removed from the container. This was a legacy method that has not been documented in several years. If you are using this method, you should begin using the `singleton` method instead: - realpath(__DIR__.'/../app/Providers/BusServiceProvider.php'), - realpath(__DIR__.'/../app/Providers/ConfigServiceProvider.php'), + $container->singleton('foo', function () { + return 'foo'; + }); -Not doing so can trigger an error when running `php artisan optimize` if the service providers listed here do not exist. +### Console -### CSRF Verification +#### The `Illuminate\Console\AppNamespaceDetectorTrait` Trait -CSRF verification is no longer automatically performed when running unit tests. This is unlikely to affect your application. +If you are directly referencing the `Illuminate\Console\AppNamespaceDetectorTrait` trait, update your code to reference `Illuminate\Console\DetectsApplicationNamespace` instead. ### Database -#### MySQL Dates - -Starting with MySQL 5.7, `0000-00-00 00:00:00` is no longer considered a valid date, since `strict` mode is enabled by default. All timestamp columns should receive a valid default value when you insert records into your database. You may use the `useCurrent` method in your migrations to default the timestamp columns to the current timestamps, or you may make the timestamps `nullable` to allow `null` values: - - $table->timestamp('foo')->nullable(); +#### Custom Connections - $table->timestamp('foo')->useCurrent(); +If you were previously binding a service container binding for a `db.connection.{driver-name}` key in order to resolve a custom database connection instance, you should now use the `Illuminate\Database\Connection::resolverFor` method in the `register` method of your `AppServiceProvider`: - $table->nullableTimestamps(); + use Illuminate\Database\Connection; -#### MySQL JSON Column Type + Connection::resolverFor('driver-name', function ($connection, $database, $prefix, $config) { + // + }); -The `json` column type now creates actual JSON columns when used by the MySQL driver. If you are not running MySQL 5.7 or above, this column type will not be available to you. Instead, use the `text` column type in your migration. +#### Fetch Mode -#### Seeding +Laravel no longer includes the ability to customize the PDO "fetch mode" from your configuration files. Instead, `PDO::FETCH_OBJ` is always used. If you will still like to customize the fetch mode for your application you may listen for the new `Illuminate\Database\Events\StatementPrepared` event: -When running database seeds, all Eloquent models are now unguarded by default. Previously a call to `Model::unguard()` was required. You can call `Model::reguard()` at the top of your `DatabaseSeeder` class if you would like models to be guarded during seeding. + Event::listen(StatementPrepared::class, function ($event) { + $event->statement->setFetchMode(...); + }); ### Eloquent #### Date Casts -Any attributes that have been added to your `$casts` property as `date` or `datetime` will now be converted to a string when `toArray` is called on the model or collection of models. This makes the date casting conversion consistent with dates specified in your `$dates` array. - -#### Global Scopes - -The global scopes implementation has been re-written to be much easier to use. Your global scopes no longer need a `remove` method, so it may be removed from any global scopes you have written. - -If you were calling `getQuery` on an Eloquent query builder to access the underlying query builder instance, you should now call `toBase`. - -If you were calling the `remove` method directly for any reason, you should change this call to `$eloquentBuilder->withoutGlobalScope($scope)`. - -New methods `withoutGlobalScope` and `withoutGlobalScopes` have been added to the Eloquent query builder. Any calls to `$model->removeGlobalScopes($builder)` may be changed to simply `$builder->withoutGlobalScopes()`. - -#### Primary keys - -By default, Eloquent assumes your primary keys are integers and will automatically cast them to integers. For any primary key that is not an integer you should override the `$incrementing` property on your Eloquent model to `false`: - - /** - * Indicates if the IDs are auto-incrementing. - * - * @var bool - */ - public $incrementing = true; - -### Events - -#### Core Event Objects - -Some of the core events fired by Laravel now use event objects instead of string event names and dynamic parameters. Below is a list of the old event names and their new object based counterparts: - -Old | New -------------- | ------------- -`artisan.start` | `Illuminate\Console\Events\ArtisanStarting` -`auth.attempting` | `Illuminate\Auth\Events\Attempting` -`auth.login` | `Illuminate\Auth\Events\Login` -`auth.logout` | `Illuminate\Auth\Events\Logout` -`cache.missed` | `Illuminate\Cache\Events\CacheMissed` -`cache.hit` | `Illuminate\Cache\Events\CacheHit` -`cache.write` | `Illuminate\Cache\Events\KeyWritten` -`cache.delete` | `Illuminate\Cache\Events\KeyForgotten` -`connection.{name}.beginTransaction` | `Illuminate\Database\Events\TransactionBeginning` -`connection.{name}.committed` | `Illuminate\Database\Events\TransactionCommitted` -`connection.{name}.rollingBack` | `Illuminate\Database\Events\TransactionRolledBack` -`illuminate.query` | `Illuminate\Database\Events\QueryExecuted` -`illuminate.queue.before` | `Illuminate\Queue\Events\JobProcessing` -`illuminate.queue.after` | `Illuminate\Queue\Events\JobProcessed` -`illuminate.queue.failed` | `Illuminate\Queue\Events\JobFailed` -`illuminate.queue.stopping` | `Illuminate\Queue\Events\WorkerStopping` -`mailer.sending` | `Illuminate\Mail\Events\MessageSending` -`router.matched` | `Illuminate\Routing\Events\RouteMatched` - -Each of these event objects contains **exactly** the same parameters that were passed to the event handler in Laravel 5.1. For example, if you were using `DB::listen` in 5.1.*, you may update your code like so for 5.2.*: - - DB::listen(function ($event) { - dump($event->sql); - dump($event->bindings); - }); - -You may check out each of the new event object classes to see their public properties. - -### Exception Handling - -Your `App\Exceptions\Handler` class' `$dontReport` property should be updated to include at least the following exception types: - - use Illuminate\Validation\ValidationException; - use Illuminate\Auth\Access\AuthorizationException; - use Illuminate\Database\Eloquent\ModelNotFoundException; - use Symfony\Component\HttpKernel\Exception\HttpException; - - /** - * A list of the exception types that should not be reported. - * - * @var array - */ - protected $dontReport = [ - AuthorizationException::class, - HttpException::class, - ModelNotFoundException::class, - ValidationException::class, - ]; - -### Helper Functions - -The `url()` helper function now returns a `Illuminate\Routing\UrlGenerator` instance when no path is provided. - -### Implicit Model Binding - -Laravel 5.2 includes "implicit model binding", a convenient new feature to automatically inject model instances into routes and controllers based on the identifier present in the URI. However, this does change the behavior of routes and controllers that type-hint model instances. - -If you were type-hinting a model instance in your route or controller and were expecting an **empty** model instance to be injected, you should remove this type-hint and create an empty model instance directly within your route or controller; otherwise, Laravel will attempt to retrieve an existing model instance from the database based on the identifier present in the route's URI. - -### IronMQ - -The IronMQ queue driver has been moved into its own package and is no longer shipped with the core framework. - -[http://github.com/LaravelCollective/iron-queue](http://github.com/laravelcollective/iron-queue) - -### Jobs / Queue - -The `php artisan make:job` command now creates a "queued" job class definition by default. If you would like to create a "sync" job, use the `--sync` option when issuing the command. - -### Mail - -The `pretend` mail configuration option has been removed. Instead, use the `log` mail driver, which performs the same function as `pretend` and logs even more information about the mail message. +The `date` cast now converts the column to a `Carbon` object and calls the `startOfDay` method on the object. If you would like to preserve the time portion of the date, you should use the `datetime` cast. -### Pagination +#### Foreign Key Conventions -To be consistent with other URLs generated by the framework, the paginator URLs no longer contain a trailing slash. This is unlikely to affect your application. +If the foreign key is not explicitly specified when defining a relationship, Eloquent will now use the table name and primary key name for the related model to build the foreign key. For the vast majority of applications, this is not a change of behavior. For example: -### Service Providers - -The `Illuminate\Foundation\Providers\ArtisanServiceProvider` should be removed from your service provider list in your `app.php` configuration file. - -The `Illuminate\Routing\ControllerServiceProvider` should be removed from your service provider list in your `app.php` configuration file. - -### Sessions - -Because of changes to the authentication system, any existing sessions will be invalidated when you upgrade to Laravel 5.2. - -#### Database Session Driver - -A new `database` session driver has been written for the framework which includes more information about the user such as their user ID, IP address, and user-agent. If you would like to continue using the old driver you may specify the `legacy-database` driver in your `session.php` configuration file. - -If you would like to use the new driver, you should add the `user_id (nullable integer)`, `ip_address (nullable string)`, and `user_agent (text)` columns to your session database table. - -### Stringy - -The "Stringy" library is no longer included with the framework. You may install it manually via Composer if you wish to use it in your application. - -### Validation - -#### Exception Types - -The `ValidatesRequests` trait now throws an instance of `Illuminate\Foundation\Validation\ValidationException` instead of throwing an instance of `Illuminate\Http\Exception\HttpResponseException`. This is unlikely to affect your application unless you were manually catching this exception. - - -### Deprecations - -The following features are deprecated in 5.2 and will be removed in the 5.3 release in June 2016: - -- `Illuminate\Contracts\Bus\SelfHandling` contract. Can be removed from jobs. -- The `lists` method on the Collection, query builder and Eloquent query builder objects has been renamed to `pluck`. The method signature remains the same. -- Implicit controller routes using `Route::controller` have been deprecated. Please use explicit route registration in your routes file. This will likely be extracted into a package. -- The `get`, `post`, and other route helper functions have been removed. You may use the `Route` facade instead. -- The `database` session driver from 5.1 has been renamed to `legacy-database` and will be removed. Consult notes on the "database session driver" above for more information. -- The `Str::randomBytes` function has been deprecated in favor of the `random_bytes` native PHP function. -- The `Str::equals` function has been deprecated in favor of the `hash_equals` native PHP function. -- `Illuminate\View\Expression` has been deprecated in favor of `Illuminate\Support\HtmlString`. -- The `WincacheStore` cache driver has been removed. - - -## Upgrading To 5.1.11 - -Laravel 5.1.11 includes support for [authorization](/docs/{{version}}/authorization) and [policies](/docs/{{version}}/authorization#policies). Incorporating these new features into your existing Laravel 5.1 applications is simple. - -> {note} These upgrades are **optional**, and ignoring them will not affect your application. - -#### Create The Policies Directory - -First, create an empty `app/Policies` directory within your application. - -#### Create / Register The AuthServiceProvider & Gate Facade - -Create a `AuthServiceProvider` within your `app/Providers` directory. You may copy the contents of the default provider [from GitHub](https://raw.githubusercontent.com/laravel/laravel/master/app/Providers/AuthServiceProvider.php). Remember to change the provider's namespace if your application is using a custom namespace. After creating the provider, be sure to register it in your `app.php` configuration file's `providers` array. - -Also, you should register the `Gate` facade in your `app.php` configuration file's `aliases` array: - - 'Gate' => Illuminate\Support\Facades\Gate::class, - -#### Update The User Model - -Secondly, use the `Illuminate\Foundation\Auth\Access\Authorizable` trait and `Illuminate\Contracts\Auth\Access\Authorizable` contract on your `App\User` model: - - belongsTo(User::class); } -#### Update The Base Controller - -Next, update your base `App\Http\Controllers\Controller` controller to use the `Illuminate\Foundation\Auth\Access\AuthorizesRequests` trait: - - -## Upgrading To 5.1.0 - -#### Estimated Upgrade Time: Less Than 1 Hour +When this is the case, Laravel will now respect your customization and determine the foreign key column name is `user_key` instead of `user_id`. -### Update `bootstrap/autoload.php` +#### Has One / Many `createMany` -Update the `$compiledPath` variable in `bootstrap/autoload.php` to the following: +The `createMany` method of a `hasOne` or `hasMany` relationship now returns a collection object instead of an array. - $compiledPath = __DIR__.'/cache/compiled.php'; +#### Related Model Connections -### Create `bootstrap/cache` Directory +Related models will now use the same connection as the parent model. For example, if you execute a query like: -Within your `bootstrap` directory, create a `cache` directory (`bootstrap/cache`). Place a `.gitignore` file in this directory with the following contents: + User::on('example')->with('posts'); - * - !.gitignore +Eloquent will query the posts table on the `example` connection instead of the default database connection. If you want to read the `posts` relationship from the default connection, you should to explicitly set the model's connection to your application's default connection. -This directory should be writable, and will be used by the framework to store temporary optimization files like `compiled.php`, `routes.php`, `config.php`, and `services.json`. +#### The `hydrate` Method -### Add `BroadcastServiceProvider` Provider +If you are currently passing a custom connection name to this method, you should now use the `on` method: -Within your `config/app.php` configuration file, add `Illuminate\Broadcasting\BroadcastServiceProvider` to the `providers` array. + User::on('connection')->hydrate($records); -### Authentication +#### `hydrateRaw` Method -If you are using the provided `AuthController` which uses the `AuthenticatesAndRegistersUsers` trait, you will need to make a few changes to how new users are validated and created. +The `Model::hydrateRaw` method has been renamed to `fromQuery`. If you are passing a custom connection name to this method, you should now use the `on` method: -First, you no longer need to pass the `Guard` and `Registrar` instances to the base constructor. You can remove these dependencies entirely from your controller's constructor. + User::on('connection')->fromQuery('...'); -Secondly, the `App\Services\Registrar` class used in Laravel 5.0 is no longer needed. You can simply copy and paste your `validator` and `create` method from this class directly into your `AuthController`. No other changes should need to be made to these methods; however, you should be sure to import the `Validator` facade and your `User` model at the top of your `AuthController`. +#### The `whereKey` method -#### Password Controller - -The included `PasswordController` no longer requires any dependencies in its constructor. You may remove both of the dependencies that were required under 5.0. - -### Validation +The `whereKey($id)` method will now add a "where" clause for the given primary key value. Previously, this would fall into the dynamic "where" clause builder and add a "where" clause for the "key" column. If you used the `whereKey` method to dynamically add a condition for the `key` column you should now use `where('key', ...)` instead. -If you are overriding the `formatValidationErrors` method on your base controller class, you should now type-hint the `Illuminate\Contracts\Validation\Validator` contract instead of the concrete `Illuminate\Validation\Validator` instance. +#### The `factory` Helper -Likewise, if you are overriding the `formatErrors` method on the base form request class, you should now type-hint `Illuminate\Contracts\Validation\Validator` contract instead of the concrete `Illuminate\Validation\Validator` instance. - -### Migrations - -If you have any migrations that rename a column or any migrations that drop columns from a SQLite database, you will need to add the `doctrine/dbal` dependency to your `composer.json` file and run the `composer update` command in your terminal to install the library. - -### Eloquent - -#### The `create` Method - -Eloquent's `create` method can now be called without any parameters. If you are overriding the `create` method in your own models, set the default value of the `$attributes` parameter to an array: - - public static function create(array $attributes = []) - { - // Your custom implementation - } - -#### The `find` Method - -If you are overriding the `find` method in your own models and calling `parent::find()` within your custom method, you should now change it to call the `find` method on the Eloquent query builder: - - public static function find($id, $columns = ['*']) - { - $model = static::query()->find($id, $columns); +Calling `factory(User::class, 1)->make()` or `factory(User::class, 1)->create()` will now return a collection with one item. Previously, this would return a single model. This method will only return a single model if the amount is not supplied. - // ... - - return $model; - } - -#### The `lists` Method - -The `lists` method now returns a `Collection` instance instead of a plain array for Eloquent queries. If you would like to convert the `Collection` into a plain array, use the `all` method: - - User::lists('id')->all(); - -Be aware that the Query Builder `lists` method still returns an array. - -#### Date Formatting - -Previously, the storage format for Eloquent date fields could be modified by overriding the `getDateFormat` method on your model. This is still possible; however, for convenience you may simply specify a `$dateFormat` property on the model instead of overriding the method. - -The date format is also now applied when serializing a model to an `array` or JSON. This may change the format of your JSON serialized date fields when migrating from Laravel 5.0 to 5.1. To set a specific date format for serialized models, you may override the `serializeDate(DateTime $date)` method on your model. This method allows you to have granular control over the formatting of serialized Eloquent date fields without changing their storage format. - -### The Collection Class - -#### The `sort` Method - -The `sort` method now returns a fresh collection instance instead of modifying the existing collection: +### Events - $collection = $collection->sort($callback); +#### Contract Changes -#### The `sortBy` Method +If you are manually implementing the `Illuminate\Contracts\Events\Dispatcher` interface in your application or package, you should rename the `fire` method to `dispatch`. -The `sortBy` method now returns a fresh collection instance instead of modifying the existing collection: +#### Event Priority - $collection = $collection->sortBy('name'); +Support for event handler "priorities" has been removed. This undocumented feature typically indicates an abuse of the event feature. Instead, consider using a series of synchronous method calls. Alternatively, you may dispatch a new event from within the handler of another event in order to ensure that a given event's handler fires after an unrelated handler. -#### The `groupBy` Method +#### Wildcard Event Handler Signatures -The `groupBy` method now returns `Collection` instances for each item in the parent `Collection`. If you would like to convert all of the items back to plain arrays, you may `map` over them: +Wildcard event handlers now receive the event name as their first argument and the array of event data as their second argument. The `Event::firing` method has been removed: - $collection->groupBy('type')->map(function($item) - { - return $item->all(); + Event::listen('*', function ($eventName, array $data) { + // }); -#### The `lists` Method - -The `lists` method now returns a `Collection` instance instead of a plain array. If you would like to convert the `Collection` into a plain array, use the `all` method: - - $collection->lists('id')->all(); - -### Commands & Handlers - -The `app/Commands` directory has been renamed to `app/Jobs`. However, you are not required to move all of your commands to the new location, and you may continue using the `make:command` and `handler:command` Artisan commands to generate your classes. - -Likewise, the `app/Handlers` directory has been renamed to `app/Listeners` and now only contains event listeners. However, you are not required to move or rename your existing command and event handlers, and you may continue to use the `handler:event` command to generate event handlers. - -By providing backwards compatibility for the Laravel 5.0 folder structure, you may upgrade your applications to Laravel 5.1 and slowly upgrade your events and commands to their new locations when it is convenient for you or your team. - -### Blade - -The `createMatcher`, `createOpenMatcher`, and `createPlainMatcher` methods have been removed from the Blade compiler. Use the new `directive` method to create custom directives for Blade in Laravel 5.1. Consult the [extending blade](/docs/{{version}}/blade#extending-blade) documentation for more information. - -### Tests - -Add the protected `$baseUrl` property to the `tests/TestCase.php` file: - - protected $baseUrl = 'http://localhost'; - -### Translation Files - -The default directory for published language files for vendor packages has been moved. Move any vendor package language files from `resources/lang/packages/{locale}/{namespace}` to `resources/lang/vendor/{namespace}/{locale}` directory. For example, `Acme/Anvil` package's `acme/anvil::foo` namespaced English language file would be moved from `resources/lang/packages/en/acme/anvil/foo.php` to `resources/lang/vendor/acme/anvil/en/foo.php`. - -### Amazon Web Services SDK - -If you are using the AWS SQS queue driver or the AWS SES e-mail driver, you should update your installed AWS PHP SDK to version 3.0. - -If you are using the Amazon S3 filesystem driver, you will need to update the corresponding Flysystem package via Composer: - -- Amazon S3: `league/flysystem-aws-s3-v3 ~1.0` - -### Deprecations - -The following Laravel features have been deprecated and will be removed entirely with the release of Laravel 5.2 in December 2015: - -
-- Route filters have been deprecated in preference of [middleware](/docs/{{version}}/middleware). -- The `Illuminate\Contracts\Routing\Middleware` contract has been deprecated. No contract is required on your middleware. In addition, the `TerminableMiddleware` contract has also been deprecated. Instead of implementing the interface, simply define a `terminate` method on your middleware. -- The `Illuminate\Contracts\Queue\ShouldBeQueued` contract has been deprecated in favor of `Illuminate\Contracts\Queue\ShouldQueue`. -- Iron.io "push queues" have been deprecated in favor of typical Iron.io queues and [queue listeners](/docs/{{version}}/queues#running-the-queue-listener). -- The `Illuminate\Foundation\Bus\DispatchesCommands` trait has been deprecated and renamed to `Illuminate\Foundation\Bus\DispatchesJobs`. -- `Illuminate\Container\BindingResolutionException` has been moved to `Illuminate\Contracts\Container\BindingResolutionException`. -- The service container's `bindShared` method has been deprecated in favor of the `singleton` method. -- The Eloquent and query builder `pluck` method has been deprecated and renamed to `value`. -- The collection `fetch` method has been deprecated in favor of the `pluck` method. -- The `array_fetch` helper has been deprecated in favor of the `array_pluck` method. -
- - -## Upgrading To 5.0.16 - -In your `bootstrap/autoload.php` file, update the `$compiledPath` variable to: - - $compiledPath = __DIR__.'/../vendor/compiled.php'; - - -### Service Providers - -The `App\Providers\BusServiceProvider` may be removed from your service provider list in your `app.php` configuration file. - -The `App\Providers\ConfigServiceProvider` may be removed from your service provider list in your `app.php` configuration file. - - - -## Upgrading To 5.0 From 4.2 - -### Fresh Install, Then Migrate - -The recommended method of upgrading is to create a new Laravel `5.0` install and then to copy your `4.2` site's unique application files into the new application. This would include controllers, routes, Eloquent models, Artisan commands, assets, and other code specific files to your application. - -To start, [install a new Laravel 5.0 application](/docs/5.0/installation) into a fresh directory in your local environment. Do not install any versions newer than 5.0 yet, since we need to complete the migration steps for 5.0 first. We'll discuss each piece of the migration process in further detail below. - -### Composer Dependencies & Packages - -Don't forget to copy any additional Composer dependencies into your 5.0 application. This includes third-party code such as SDKs. - -Some Laravel-specific packages may not be compatible with Laravel 5 on initial release. Check with your package's maintainer to determine the proper version of the package for Laravel 5. Once you have added any additional Composer dependencies your application needs, run `composer update`. - -### Namespacing - -By default, Laravel 4 applications did not utilize namespacing within your application code. So, for example, all Eloquent models and controllers simply lived in the "global" namespace. For a quicker migration, you can simply leave these classes in the global namespace in Laravel 5 as well. - -### Configuration +#### The `kernel.handled` Event -#### Migrating Environment Variables +The `kernel.handled` event is now an object based event using the `Illuminate\Foundation\Http\Events\RequestHandled` class. -Copy the new `.env.example` file to `.env`, which is the `5.0` equivalent of the old `.env.php` file. Set any appropriate values there, like your `APP_ENV` and `APP_KEY` (your encryption key), your database credentials, and your cache and session drivers. +#### The `locale.changed` Event -Additionally, copy any custom values you had in your old `.env.php` file and place them in both `.env` (the real value for your local environment) and `.env.example` (a sample instructional value for other team members). +The `locale.changed` event is now an object based event using the `Illuminate\Foundation\Events\LocaleUpdated` class. -For more information on environment configuration, view the [full documentation](/docs/{{version}}/installation#environment-configuration). +#### The `illuminate.log` Event -> {note} You will need to place the appropriate `.env` file and values on your production server before deploying your Laravel 5 application. +The `illuminate.log` event is now an object based event using the `Illuminate\Log\Events\MessageLogged` class. -#### Configuration Files +### Exceptions -Laravel 5.0 no longer uses `app/config/{environmentName}/` directories to provide specific configuration files for a given environment. Instead, move any configuration values that vary by environment into `.env`, and then access them in your configuration files using `env('key', 'default value')`. You will see examples of this in the `config/database.php` configuration file. +The `Illuminate\Http\Exception\HttpResponseException` has been renamed to `Illuminate\Http\Exceptions\HttpResponseException`. Note that `Exceptions` is now plural. Likewise, the `Illuminate\Http\Exception\PostTooLargeException` has been renamed to `Illuminate\Http\Exceptions\PostTooLargeException`. -Set the config files in the `config/` directory to represent either the values that are consistent across all of your environments, or set them to use `env()` to load values that vary by environment. - -Remember, if you add more keys to `.env` file, add sample values to the `.env.example` file as well. This will help your other team members create their own `.env` files. - -### Routes - -Copy and paste your old `routes.php` file into your new `app/Http/routes.php`. - -### Controllers - -Next, move all of your controllers into the `app/Http/Controllers` directory. Since we are not going to migrate to full namespacing in this guide, add the `app/Http/Controllers` directory to the `classmap` directive of your `composer.json` file. Next, you can remove the namespace from the abstract `app/Http/Controllers/Controller.php` base class. Verify that your migrated controllers are extending this base class. - -In your `app/Providers/RouteServiceProvider.php` file, set the `namespace` property to `null`. - -### Route Filters - -Copy your filter bindings from `app/filters.php` and place them into the `boot()` method of `app/Providers/RouteServiceProvider.php`. Add `use Illuminate\Support\Facades\Route;` in the `app/Providers/RouteServiceProvider.php` in order to continue using the `Route` Facade. - -You do not need to move over any of the default Laravel 4.0 filters such as `auth` and `csrf`; they're all here, but as middleware. Edit any routes or controllers that reference the old default filters (e.g. `['before' => 'auth']`) and change them to reference the new middleware (e.g. `['middleware' => 'auth'].`) - -Filters are not removed in Laravel 5. You can still bind and use your own custom filters using `before` and `after`. - -### Global CSRF - -By default, [CSRF protection](/docs/{{version}}/routing#csrf-protection) is enabled on all routes. If you'd like to disable this, or only manually enable it on certain routes, remove this line from `App\Http\Kernel`'s `middleware` array: - - 'App\Http\Middleware\VerifyCsrfToken', - -If you want to use it elsewhere, add this line to `$routeMiddleware`: - - 'csrf' => 'App\Http\Middleware\VerifyCsrfToken', - -Now you can add the middleware to individual routes / controllers using `['middleware' => 'csrf']` on the route. For more information on middleware, consult the [full documentation](/docs/{{version}}/middleware). - -### Eloquent Models - -Feel free to create a new `app/Models` directory to house your Eloquent models. Again, add this directory to the `classmap` directive of your `composer.json` file. - -Update any models using `SoftDeletingTrait` to use `Illuminate\Database\Eloquent\SoftDeletes`. - -#### Eloquent Caching - -Eloquent no longer provides the `remember` method for caching queries. You now are responsible for caching your queries manually using the `Cache::remember` function. For more information on caching, consult the [full documentation](/docs/{{version}}/cache). - -### User Authentication Model - -To upgrade your `User` model for Laravel 5's authentication system, follow these instructions: - -**Delete the following from your `use` block:** - -```php -use Illuminate\Auth\UserInterface; -use Illuminate\Auth\Reminders\RemindableInterface; -``` - -**Add the following to your `use` block:** - -```php -use Illuminate\Auth\Authenticatable; -use Illuminate\Auth\Passwords\CanResetPassword; -use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; -use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; -``` - -**Remove the UserInterface and RemindableInterface interfaces.** - -**Mark the class as implementing the following interfaces:** - -```php -implements AuthenticatableContract, CanResetPasswordContract -``` - -**Include the following traits within the class declaration:** - -```php -use Authenticatable, CanResetPassword; -``` - -**If you used them, remove `Illuminate\Auth\Reminders\RemindableTrait` and `Illuminate\Auth\UserTrait` from your use block and your class declaration.** - -### Cashier User Changes - -The name of the trait and interface used by [Laravel Cashier](/docs/{{version}}/billing) has changed. Instead of using `BillableTrait`, use the `Laravel\Cashier\Billable` trait. And, instead of `Laravel\Cashier\BillableInterface` implement the `Laravel\Cashier\Contracts\Billable` interface instead. No other method changes are required. - -### Artisan Commands - -Move all of your command classes from your old `app/commands` directory to the new `app/Console/Commands` directory. Next, add the `app/Console/Commands` directory to the `classmap` directive of your `composer.json` file. - -Then, copy your list of Artisan commands from `start/artisan.php` into the `commands` array of the `app/Console/Kernel.php` file. - -### Database Migrations & Seeds - -Delete the two migrations included with Laravel 5.0, since you should already have the users table in your database. - -Move all of your migration classes from the old `app/database/migrations` directory to the new `database/migrations`. All of your seeds should be moved from `app/database/seeds` to `database/seeds`. - -### Global IoC Bindings - -If you have any [service container](/docs/{{version}}/container) bindings in `start/global.php`, move them all to the `register` method of the `app/Providers/AppServiceProvider.php` file. You may need to import the `App` facade. - -Optionally, you may break these bindings up into separate service providers by category. - -### Views - -Move your views from `app/views` to the new `resources/views` directory. - -### Blade Tag Changes - -For better security by default, Laravel 5.0 escapes all output from both the `{{ }}` and `{{{ }}}` Blade directives. A new `{!! !!}` directive has been introduced to display raw, unescaped output. The most secure option when upgrading your application is to only use the new `{!! !!}` directive when you are **certain** that it is safe to display raw output. - -However, if you **must** use the old Blade syntax, add the following lines at the bottom of `AppServiceProvider@register`: - -```php -\Blade::setRawTags('{{', '}}'); -\Blade::setContentTags('{{{', '}}}'); -\Blade::setEscapedContentTags('{{{', '}}}'); -``` - -This should not be done lightly, and may make your application more vulnerable to XSS exploits. Also, comments with `{{--` will no longer work. - -### Translation Files +### Mail -Move your language files from `app/lang` to the new `resources/lang` directory. +#### `Class@method` Syntax -### Public Directory +Sending mail using `Class@method` syntax is no longer supported. For example: -Copy your application's public assets from your `4.2` application's `public` directory to your new application's `public` directory. Be sure to keep the `5.0` version of `index.php`. + Mail::send('view.name', $data, 'Class@send'); -### Tests +If you are sending mail in this way you should convert these calls to [mailables](/docs/{{version}}/mail). -Move your tests from `app/tests` to the new `tests` directory. +#### New Configuration Options -### Misc. Files +In order to provide support for Laravel 5.4's new Markdown mail components, you should add the following block of configuration to the bottom of your `mail` configuration file: -Copy in any other files in your project. For example, `.scrutinizer.yml`, `bower.json` and other similar tooling configuration files. + 'markdown' => [ + 'theme' => 'default', -You may move your Sass, Less, or CoffeeScript to any location you wish. The `resources/assets` directory could be a good default location. + 'paths' => [ + resource_path('views/vendor/mail'), + ], + ], -### Form & HTML Helpers +#### Queueing Mail With Closures -If you're using Form or HTML helpers, you will see an error stating `class 'Form' not found` or `class 'Html' not found`. The Form and HTML helpers have been deprecated in Laravel 5.0; however, there are community-driven replacements such as those maintained by the [Laravel Collective](http://laravelcollective.com/docs/{{version}}/html). +In order to queue mail, you now must use a [mailable](/docs/{{version}}/mail). Queuing mail using the `Mail::queue` and `Mail::later` methods no longer supports using Closures to configure the mail message. This feature required the use of special libraries to serialize Closures since PHP does not natively support this feature. -For example, you may add `"laravelcollective/html": "~5.0"` to your `composer.json` file's `require` section. +### Redis -You'll also need to add the Form and HTML facades and service provider. Edit `config/app.php` and add this line to the 'providers' array: +#### Improved Clustering Support - 'Collective\Html\HtmlServiceProvider', +Laravel 5.4 introduces improved Redis cluster support. If you are using Redis clusters, you should place your cluster connections inside of a `clusters` configuration option in the Redis portion of your `config/database.php` configuration file: -Next, add these lines to the 'aliases' array: + 'redis' => [ - 'Form' => 'Collective\Html\FormFacade', - 'Html' => 'Collective\Html\HtmlFacade', + 'client' => 'predis', -### CacheManager + 'options' => [ + 'cluster' => 'redis', + ], -If your application code was injecting `Illuminate\Cache\CacheManager` to get a non-Facade version of Laravel's cache, inject `Illuminate\Contracts\Cache\Repository` instead. + 'clusters' => [ + 'default' => [ + [ + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'password' => env('REDIS_PASSWORD', null), + 'port' => env('REDIS_PORT', 6379), + 'database' => 0, + ], + ], + ], -### Pagination + ], -Replace any calls to `$paginator->links()` with `$paginator->render()`. +### Routing -Replace any calls to `$paginator->getFrom()` and `$paginator->getTo()` with `$paginator->firstItem()` and `$paginator->lastItem()` respectively. +#### Post Size Middleware -Remove the "get" prefix from calls to `$paginator->getPerPage()`, `$paginator->getCurrentPage()`, `$paginator->getLastPage()` and `$paginator->getTotal()` (e.g. `$paginator->perPage()`). +The class `Illuminate\Foundation\Http\Middleware\VerifyPostSize` has been renamed to `Illuminate\Foundation\Http\Middleware\ValidatePostSize`. -### Beanstalk Queuing +#### The `middleware` Method -Laravel 5.0 now requires `"pda/pheanstalk": "~3.0"` instead of `"pda/pheanstalk": "~2.1"`. +The `middleware` method of the `Illuminate\Routing\Router` class has been renamed to `aliasMiddleware()`. It is likely that most applications never call this method manually, as it is typically only called by the HTTP kernel to register route-level middleware defined in the `$routeMiddleware` array. -### Remote +#### The `getParameter` Method -The Remote component has been deprecated. +The `getParameter` method of the `Illuminate\Routing\Route` class has been removed. You should use the `parameter` method instead. -### Workbench +### Sessions -The Workbench component has been deprecated. +#### Symfony Compatibility - -## Upgrading To 4.2 From 4.1 +Laravel's session handlers no longer implements Symfony's `SessionInterface`. Implementing this interface required us to implement extraneous features that were not needed by the framework. Instead, a new `Illuminate\Contracts\Session\Session` interface has been defined and may be used instead. The following code changes should also be applied: -### PHP 5.4+ +All calls to the `->set()` method should be changed to `->put()`. Typically, Laravel applications would never call the `set` method since it has never been documented within the Laravel documentation. However, it is included here out of caution. -Laravel 4.2 requires PHP 5.4.0 or greater. +All calls to the `->getToken()` method should be changed to `->token()`. -### Encryption Defaults +All calls to the `$request->setSession()` method should be changed to `setLaravelSession()`. -Add a new `cipher` option in your `app/config/app.php` configuration file. The value of this option should be `MCRYPT_RIJNDAEL_256`. +### Testing - 'cipher' => MCRYPT_RIJNDAEL_256 +Laravel 5.4's testing layer has been re-written to be simpler and lighter out of the box. If you would like to continue using the testing layer present in Laravel 5.3, you may install the `laravel/browser-kit-testing` [package](https://github.com/laravel/browser-kit-testing) into your application. This package provides full compatibility with the Laravel 5.3 testing layer. In fact, you can run the Laravel 5.4 testing layer side-by-side with the Laravel 5.3 testing layer. -This setting may be used to control the default cipher used by the Laravel encryption facilities. +If you have tests written using Laravel 5.3 and would like to run them side-by-side with Laravel's new testing layer, install the `laravel/browser-kit-testing` package: -> {note} In Laravel 4.2, the default cipher is `MCRYPT_RIJNDAEL_128` (AES), which is considered to be the most secure cipher. Changing the cipher back to `MCRYPT_RIJNDAEL_256` is required to decrypt cookies/values that were encrypted in Laravel <= 4.1 + composer require laravel/browser-kit-testing -### Soft Deleting Models Now Use Traits +Next, create a copy of your `tests/TestCase.php` file and save it to your `tests` directory as `BrowserKitTest.php`. Then, modify the file to extend the `Laravel\BrowserKitTesting\TestCase` class. Once you have done this, you should have two base test classes in your `tests` directory: `TestCase.php` and `BrowserKitTest.php`. In order for your `BrowserKitTest` class to be properly loaded, you may need to add it to your `composer.json` file: -If you are using soft deleting models, the `softDeletes` property has been removed. You must now use the `SoftDeletingTrait` like so: + "autoload-dev": { + "classmap": [ + "tests/TestCase.php", + "tests/BrowserKitTest.php" + ] + }, - use Illuminate\Database\Eloquent\SoftDeletingTrait; +Tests written on Laravel 5.3 will extend the `BrowserKitTest` class while any new tests that use the Laravel 5.4 testing layer will extend the `TestCase` class. Your `BrowserKitTest` class should look like the following: - class User extends Eloquent - { - use SoftDeletingTrait; - } + make(Kernel::class)->bootstrap(); + + return $app; + } } -The API for all soft delete operations remains the same. - -> {note} The `SoftDeletingTrait` can not be applied on a base model. It must be used on an actual model class. - -### View / Pagination Environment Renamed - -If you are directly referencing the `Illuminate\View\Environment` class or `Illuminate\Pagination\Environment` class, update your code to reference `Illuminate\View\Factory` and `Illuminate\Pagination\Factory` instead. These two classes have been renamed to better reflect their function. - -### Additional Parameter On Pagination Presenter - -If you are extending the `Illuminate\Pagination\Presenter` class, the abstract method `getPageLinkWrapper` signature has changed to add the `rel` argument: - - abstract public function getPageLinkWrapper($url, $page, $rel = null); - -### Iron.Io Queue Encryption - -If you are using the Iron.io queue driver, you will need to add a new `encrypt` option to your queue configuration file: - - 'encrypt' => true +Once you have created this class, make sure to update all of your tests to extend your new `BrowserKitTest` class. This will allow all of your tests written on Laravel 5.3 to continue running on Laravel 5.4. If you choose, you can slowly begin to port them over to the new [Laravel 5.4 test syntax](/docs/5.4/http-tests) or [Laravel Dusk](/docs/5.4/dusk). - -## Upgrading To 4.1.29 From <= 4.1.x +> {note} If you are writing new tests and want them to use the Laravel 5.4 testing layer, make sure to extend the `TestCase` class. -Laravel 4.1.29 improves the column quoting for all database drivers. This protects your application from some mass assignment vulnerabilities when **not** using the `fillable` property on models. If you are using the `fillable` property on your models to protect against mass assignment, your application is not vulnerable. However, if you are using `guarded` and are passing a user controlled array into an "update" or "save" type function, you should upgrade to `4.1.29` immediately as your application may be at risk of mass assignment. +#### Installing Dusk In An Upgraded Application -To upgrade to Laravel 4.1.29, simply `composer update`. No breaking changes are introduced in this release. +If you would like to install Laravel Dusk into an application that has been upgraded from Laravel 5.3, first install it via Composer: - -## Upgrading To 4.1.26 From <= 4.1.25 + composer require laravel/dusk -Laravel 4.1.26 introduces security improvements for "remember me" cookies. Before this update, if a remember cookie was hijacked by another malicious user, the cookie would remain valid for a long period of time, even after the true owner of the account reset their password, logged out, etc. +Next, you will need to create a `CreatesApplication` trait in your `tests` directory. This trait is responsible for creating fresh application instances for test cases. The trait should look like the following: -This change requires the addition of a new `remember_token` column to your `users` (or equivalent) database table. After this change, a fresh token will be assigned to the user each time they login to your application. The token will also be refreshed when the user logs out of the application. The implications of this change are: if a "remember me" cookie is hijacked, simply logging out of the application will invalidate the cookie. - -### Upgrade Path - -First, add a new, nullable `remember_token` of VARCHAR(100), TEXT, or equivalent to your `users` table. - -Next, if you are using the Eloquent authentication driver, update your `User` class with the following three methods: - - public function getRememberToken() - { - return $this->remember_token; - } + remember_token = $value; - } + use Illuminate\Contracts\Console\Kernel; - public function getRememberTokenName() + trait CreatesApplication { - return 'remember_token'; + /** + * Creates the application. + * + * @return \Illuminate\Foundation\Application + */ + public function createApplication() + { + $app = require __DIR__.'/../bootstrap/app.php'; + + $app->make(Kernel::class)->bootstrap(); + + return $app; + } } -> {note} All existing "remember me" sessions will be invalidated by this change, so all users will be forced to re-authenticate with your application. - -### Package Maintainers - -Two new methods were added to the `Illuminate\Auth\UserProviderInterface` interface. Sample implementations may be found in the default drivers: - - public function retrieveByToken($identifier, $token); - - public function updateRememberToken(UserInterface $user, $token); +> {note} If you have namespaced your tests and are using the PSR-4 autoloading standard to load your `tests` directory, you should place the `CreatesApplication` trait under the appropriate namespace. -The `Illuminate\Auth\UserInterface` also received the three new methods described in the "Upgrade Path". +Once you have completed these preparatory steps, you can follow the normal [Dusk installation instructions](/docs/{{version}}/dusk#installation). - -## Upgrading To 4.1 From 4.0 +#### Environment -### Upgrading Your Composer Dependency +The Laravel 5.4 test class no longer manually forces `putenv('APP_ENV=testing')` for each test. Instead, the framework utilizes the `APP_ENV` variable from the loaded `.env` file. -To upgrade your application to Laravel 4.1, change your `laravel/framework` version to `4.1.*` in your `composer.json` file. +#### Event Fake -### Replacing Files +The `Event` fake's `assertFired` method should be updated to `assertDispatched`. The method signature has not been changed. -Replace your `public/index.php` file with [this fresh copy from the repository](https://github.com/laravel/laravel/blob/v4.1.0/public/index.php). +#### Mail Fake -Replace your `artisan` file with [this fresh copy from the repository](https://github.com/laravel/laravel/blob/v4.1.0/artisan). +The `Mail` fake has been greatly simplified for the Laravel 5.4 release. Instead of using the `assertSentTo` method, you should now simply use the `assertSent` method and utilize the `hasTo`, `hasCc`, etc. helper methods within your callback: -### Adding Configuration Files & Options - -Update your `aliases` and `providers` arrays in your `app/config/app.php` configuration file. The updated values for these arrays can be found [in this file](https://github.com/laravel/laravel/blob/v4.1.0/app/config/app.php). Be sure to add your custom and package service providers / aliases back to the arrays. - -Add the new `app/config/remote.php` file [from the repository](https://github.com/laravel/laravel/blob/v4.1.0/app/config/remote.php). - -Add the new `expire_on_close` configuration option to your `app/config/session.php` file. The default value should be `false`. - -Add the new `failed` configuration section to your `app/config/queue.php` file. Here are the default values for the section: - - 'failed' => [ - 'database' => 'mysql', 'table' => 'failed_jobs', - ], - -**(Optional)** Update the `pagination` configuration option in your `app/config/view.php` file to `pagination::slider-3`. - -### Controller Updates - -If `app/controllers/BaseController.php` has a `use` statement at the top, change `use Illuminate\Routing\Controllers\Controller;` to `use Illuminate\Routing\Controller;`. - -### Password Reminders Updates - -Password reminders have been overhauled for greater flexibility. You may examine the new stub controller by running the `php artisan auth:reminders-controller` Artisan command. You may also browse the [updated documentation](/docs/security#password-reminders-and-reset) and update your application accordingly. + Mail::assertSent(MailableName::class, function ($mailable) { + return $mailable->hasTo('email@example.com'); + }); -Update your `app/lang/en/reminders.php` language file to match [this updated file](https://github.com/laravel/laravel/blob/v4.1.0/app/lang/en/reminders.php). +### Translation -### Environment Detection Updates +#### `{Inf}` Placeholder -For security reasons, URL domains may no longer be used to detect your application environment. These values are easily spoofable and allow attackers to modify the environment for a request. You should convert your environment detection to use machine host names (`hostname` command on Mac, Linux, and Windows). +If you are using the `{Inf}` placeholder for pluralizing your translation strings, you should update your translation strings to use the `*` character instead: -### Simpler Log Files + {0} First Message|{1,*} Second Message -Laravel now generates a single log file: `app/storage/logs/laravel.log`. However, you may still configure this behavior in your `app/start/global.php` file. +### URL Generation -### Removing Redirect Trailing Slash +#### The `forceSchema` Method -In your `bootstrap/start.php` file, remove the call to `$app->redirectIfTrailingSlash()`. This method is no longer needed as this functionality is now handled by the `.htaccess` file included with the framework. +The `forceSchema` method of the `Illuminate\Routing\UrlGenerator` class has been renamed to `forceScheme`. -Next, replace your Apache `.htaccess` file with [this new one](https://github.com/laravel/laravel/blob/v4.1.0/public/.htaccess) that handles trailing slashes. +### Validation -### Current Route Access +#### Date Format Validation -The current route is now accessed via `Route::current()` instead of `Route::getCurrentRoute()`. +Date format validation is now more strict and supports the placeholders present within the documentation for the PHP [date function](http://php.net/manual/en/function.date.php). In previous releases of Laravel, the timezone placeholder `P` would accept all timezone formats; however, in Laravel 5.4 each timezone format has a unique placeholder as per the PHP documentation. -### Composer Update +#### Method Names -Once you have completed the changes above, you can run the `composer update` function to update your core application files! If you receive class load errors, try running the `update` command with the `--no-scripts` option enabled like so: `composer update --no-scripts`. +The `addError` method has been renamed to `addFailure`. In addition, the `doReplacements` method has been renamed to `makeReplacements`. Typically, these changes will only be relevant if you are extending the `Validator` class. -### Wildcard Event Listeners +### Miscellaneous -The wildcard event listeners no longer append the event to your handler functions parameters. If you require finding the event that was fired you should use `Event::firing()`. +We also encourage you to view the changes in the `laravel/laravel` [GitHub repository](https://github.com/laravel/laravel). While many of these changes are not required, you may wish to keep these files in sync with your application. Some of these changes will be covered in this upgrade guide, but others, such as changes to configuration files or comments, will not be. You can easily view the changes with the [Github comparison tool](https://github.com/laravel/laravel/compare/5.3...master) and choose which updates are important to you. diff --git a/valet.md b/valet.md index b7ff823..73d9741 100644 --- a/valet.md +++ b/valet.md @@ -3,22 +3,21 @@ - [Introduction](#introduction) - [Valet Or Homestead](#valet-or-homestead) - [Installation](#installation) -- [Release Notes](#release-notes) + - [Upgrading](#upgrading) - [Serving Sites](#serving-sites) - [The "Park" Command](#the-park-command) - [The "Link" Command](#the-link-command) - [Securing Sites With TLS](#securing-sites) - [Sharing Sites](#sharing-sites) -- [Viewing Logs](#viewing-logs) - [Custom Valet Drivers](#custom-valet-drivers) - [Other Valet Commands](#other-valet-commands) ## Introduction -Valet is a Laravel development environment for Mac minimalists. No Vagrant, No Apache, No Nginx, No `/etc/hosts` file. You can even share your sites publicly using local tunnels. _Yeah, we like it too._ +Valet is a Laravel development environment for Mac minimalists. No Vagrant, no `/etc/hosts` file. You can even share your sites publicly using local tunnels. _Yeah, we like it too._ -Laravel Valet configures your Mac to always run [Caddy](https://caddyserver.com) in the background when your machine starts. Then, using [DnsMasq](https://en.wikipedia.org/wiki/Dnsmasq), Valet proxies all requests on the `*.dev` domain to point to sites installed on your local machine. +Laravel Valet configures your Mac to always run [Nginx](https://www.nginx.com/) in the background when your machine starts. Then, using [DnsMasq](https://en.wikipedia.org/wiki/Dnsmasq), Valet proxies all requests on the `*.dev` domain to point to sites installed on your local machine. In other words, a blazing fast Laravel development environment that uses roughly 7 MB of RAM. Valet isn't a complete replacement for Vagrant or Homestead, but provides a great alternative if you want flexible basics, prefer extreme speed, or are working on a machine with a limited amount of RAM. @@ -28,10 +27,10 @@ Out of the box, Valet support includes, but is not limited to: - [Laravel](https://laravel.com) - [Lumen](https://lumen.laravel.com) - [Symfony](https://symfony.com) -- [Zend](http://framework.zend.com) -- [CakePHP 3](http://cakephp.org) +- [Zend](https://framework.zend.com) +- [CakePHP 3](https://cakephp.org) - [WordPress](https://wordpress.org) -- [Bedrock](https://roots.io/bedrock) +- [Bedrock](https://roots.io/bedrock/) - [Craft](https://craftcms.com) - [Statamic](https://statamic.com) - [Jigsaw](http://jigsaw.tighten.co) @@ -45,7 +44,7 @@ However, you may extend Valet with your own [custom drivers](#custom-valet-drive As you may know, Laravel offers [Homestead](/docs/{{version}}/homestead), another local Laravel development environment. Homestead and Valet differ in regards to their intended audience and their approach to local development. Homestead offers an entire Ubuntu virtual machine with automated Nginx configuration. Homestead is a wonderful choice if you want a fully virtualized Linux development environment or are on Windows / Linux. -Valet only supports Mac, and requires you to install PHP and a database server directly onto your local machine. This is easily achieved by using [Homebrew](http://brew.sh/) with commands like `brew install php70` and `brew install mariadb`. Valet provides a blazing fast local development environment with minimal resource consumption, so it's great for developers who only require PHP / MySQL and do not need a fully virtualized development environment. +Valet only supports Mac, and requires you to install PHP and a database server directly onto your local machine. This is easily achieved by using [Homebrew](http://brew.sh/) with commands like `brew install php71` and `brew install mysql`. Valet provides a blazing fast local development environment with minimal resource consumption, so it's great for developers who only require PHP / MySQL and do not need a fully virtualized development environment. Both Valet and Homestead are great choices for configuring your Laravel development environment. Which one you choose will depend on your personal taste and your team's needs. @@ -56,7 +55,7 @@ Both Valet and Homestead are great choices for configuring your Laravel developm
- Install or update [Homebrew](http://brew.sh/) to the latest version using `brew update`. -- Install PHP 7.0 using Homebrew via `brew install homebrew/php/php70`. +- Install PHP 7.1 using Homebrew via `brew install homebrew/php/php71`. - Install Valet with Composer via `composer global require laravel/valet`. Make sure the `~/.composer/vendor/bin` directory is in your system's "PATH". - Run the `valet install` command. This will configure and install Valet and DnsMasq, and register Valet's daemon to launch when your system starts.
@@ -73,26 +72,30 @@ For example, if you'd like to use `.app` instead of `.dev`, run `valet domain ap #### Database -If you need a database, try MariaDB by running `brew install mariadb` on your command line. You can connect to the database at `127.0.0.1` using the `root` username and an empty string for the password. +If you need a database, try MySQL by running `brew install mysql` on your command line. Once MySQL has been installed, you may start it using the `brew services start mysql` command. You can then connect to the database at `127.0.0.1` using the `root` username and an empty string for the password. - -## Release Notes + +### Upgrading -### Version 1.1.5 +You may update your Valet installation using the `composer global update` command in your terminal. After upgrading, it is good practice to run the `valet install` command so Valet can make additional upgrades to your configuration files if necessary. -The 1.1.5 release of Valet brings a variety of internal improvements. +#### Upgrading To Valet 2.0 -#### Upgrade Instructions +Valet 2.0 transitions Valet's underlying web server from Caddy to Nginx. Before upgrading to this version you should run the following commands to stop and uninstall the existing Caddy daemon: -After updating your Valet installation using `composer global update`, you should run the `valet install` command in your terminal. + valet stop + valet uninstall -### Version 1.1.0 +Next, you should upgrade to the latest version of Valet. Depending on how you installed Valet, this is typically done through Git or Composer. If you installed Valet via Composer, you should use the following command to update to the latest major version: -The 1.1.0 release of Valet brings a variety of great improvements. The built-in PHP server has been replaced with [Caddy](https://caddyserver.com/) for serving incoming HTTP requests. Introducing Caddy allows for a variety of future improvements and allows Valet sites to make HTTP requests to other Valet sites without blocking the built-in PHP server. + composer global require laravel/valet -#### Upgrade Instructions +Once the fresh Valet source code has been downloaded, you should run the `install` command: -After updating your Valet installation using `composer global update`, you should run the `valet install` command in your terminal to create the new Caddy daemon file on your system. + valet install + valet restart + +After upgrading, it may be necessary to re-park or re-link your sites. ## Serving Sites @@ -122,6 +125,8 @@ The `link` command may also be used to serve your Laravel sites. This command is To see a listing of all of your linked directories, run the `valet links` command. You may use `valet unlink app-name` to destroy the symbolic link. +> {tip} You can use `valet link` to serve the same project from multiple (sub)domains. To add a subdomain or another domain to your project run `valet link subdomain.app-name` from the project folder. + **Securing Sites With TLS** @@ -142,10 +147,7 @@ To share a site, navigate to the site's directory in your terminal and run the ` To stop sharing your site, hit `Control + C` to cancel the process. - -## Viewing Logs - -If you would like to stream all of the logs for all of your sites to your terminal, run the `valet logs` command. New log entries will display in your terminal as they occur. This is a great way to stay on top of all of your log files without ever having to leave your terminal. +> {note} `valet share` does not currently support sharing sites that have been secured using the `valet secure` command. ## Custom Valet Drivers @@ -156,7 +158,7 @@ All three methods receive the `$sitePath`, `$siteName`, and `$uri` values as the Once you have completed your custom Valet driver, place it in the `~/.valet/Drivers` directory using the `FrameworkValetDriver.php` naming convention. For example, if you are writing a custom valet driver for WordPress, your file name should be `WordPressValetDriver.php`. -Let's take at a sample implementation of each method your custom Valet driver should implement. +Let's take a look at a sample implementation of each method your custom Valet driver should implement. #### The `serves` Method @@ -170,7 +172,7 @@ For example, let's pretend we are writing a `WordPressValetDriver`. Our serve me * @param string $sitePath * @param string $siteName * @param string $uri - * @return void + * @return bool */ public function serves($sitePath, $siteName, $uri) { diff --git a/validation.md b/validation.md index 0440501..d4fd973 100644 --- a/validation.md +++ b/validation.md @@ -226,10 +226,29 @@ So, how are the validation rules evaluated? All you need to do is type-hint the If validation fails, a redirect response will be generated to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an AJAX request, a HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors. +#### Adding After Hooks To Form Requests + +If you would like to add an "after" hook to a form request, you may use the `withValidator` method. This method receives the fully constructed validator, allowing you to call any of its methods before the validation rules are actually evaluated: + + /** + * Configure the validator instance. + * + * @param \Illuminate\Validation\Validator $validator + * @return void + */ + public function withValidator($validator) + { + $validator->after(function ($validator) { + if ($this->somethingElseIsInvalid()) { + $validator->errors()->add('field', 'Something is wrong with this field!'); + } + }); + } + ### Authorizing Form Requests -The form request class also contains an `authorize` method. Within this method, you may check if the authenticated user actually has the authority to update a given resource. For example, if a user is attempting to update a blog post comment, do they actually own that comment? For example: +The form request class also contains an `authorize` method. Within this method, you may check if the authenticated user actually has the authority to update a given resource. For example, you may determine if a user actually owns a blog comment they are attempting to update: /** * Determine if the user is authorized to make this request. @@ -332,7 +351,7 @@ If you do not want to use the `ValidatesRequests` trait's `validate` method, you The first argument passed to the `make` method is the data under validation. The second argument is the validation rules that should be applied to the data. -After checking if the request passed validation, you may use the `withErrors` method to flash the error messages to the session. When using this method, the `$errors` variable will automatically be shared with your views after redirection, allowing you to easily display them back to the user. The `withErrors` method accepts a validator, a `MessageBag`, or a PHP `array`. +After checking if the request validation failed, you may use the `withErrors` method to flash the error messages to the session. When using this method, the `$errors` variable will automatically be shared with your views after redirection, allowing you to easily display them back to the user. The `withErrors` method accepts a validator, a `MessageBag`, or a PHP `array`. ### Automatic Redirection @@ -363,7 +382,7 @@ The validator also allows you to attach callbacks to be run after validation is $validator = Validator::make(...); - $validator->after(function($validator) { + $validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add('field', 'Something is wrong with this field!'); } @@ -394,6 +413,12 @@ If you need to retrieve an array of all the messages for a given field, use the // } +If you are validating an array form field, you may retrieve all of the messages for each of the array elements using the `*` character: + + foreach ($errors->get('attachments.*') as $message) { + // + } + #### Retrieving All Error Messages For All Fields To retrieve an array of all messages for all fields, use the `all` method: @@ -449,6 +474,14 @@ In most cases, you will probably specify your custom messages in a language file ], ], +#### Specifying Custom Attributes In Language Files + +If you would like the `:attribute` portion of your validation message to be replaced with a custom attribute name, you may specify the custom name in the `attributes` array of your `resources/lang/xx/validation.php` language file: + + 'attributes' => [ + 'email' => 'email address', + ], + ## Available Validation Rules @@ -470,11 +503,13 @@ Below is a list of all available validation rules and their function: [Accepted](#rule-accepted) [Active URL](#rule-active-url) [After (Date)](#rule-after) +[After Or Equal (Date)](#rule-after-or-equal) [Alpha](#rule-alpha) [Alpha Dash](#rule-alpha-dash) [Alpha Numeric](#rule-alpha-num) [Array](#rule-array) [Before (Date)](#rule-before) +[Before Or Equal (Date)](#rule-before-or-equal) [Between](#rule-between) [Boolean](#rule-boolean) [Confirmed](#rule-confirmed) @@ -528,7 +563,7 @@ The field under validation must be _yes_, _on_, _1_, or _true_. This is useful f #### active_url -The field under validation must be a valid URL according to the `checkdnsrr` PHP function. +The field under validation must have a valid A or AAAA record according to the `dns_get_record` PHP function. #### after:_date_ @@ -541,6 +576,11 @@ Instead of passing a date string to be evaluated by `strtotime`, you may specify 'finish_date' => 'required|date|after:start_date' + +#### after\_or\_equal:_date_ + +The field under validation must be a value after or equal to the given date. For more information, see the [after](#rule-after) rule. + #### alpha @@ -566,6 +606,11 @@ The field under validation must be a PHP `array`. The field under validation must be a value preceding the given date. The dates will be passed into the PHP `strtotime` function. + +#### before\_or\_equal:_date_ + +The field under validation must be a value preceding or equal to the given date. The dates will be passed into the PHP `strtotime` function. + #### between:_min_,_max_ @@ -589,7 +634,7 @@ The field under validation must be a valid date according to the `strtotime` PHP #### date_format:_format_ -The field under validation must match the given _format_. The format will be evaluated using the PHP `date_parse_from_format` function. You should use **either** `date` or `date_format` when validating a field, not both. +The field under validation must match the given _format_. You should use **either** `date` or `date_format` when validating a field, not both. #### different:_field_ @@ -619,6 +664,17 @@ A _ratio_ constraint should be represented as width divided by height. This can 'avatar' => 'dimensions:ratio=3/2' +Since this rule requires several arguments, you may use the `Rule::dimensions` method to fluently construct the rule: + + use Illuminate\Validation\Rule; + + Validator::make($data, [ + 'avatar' => [ + 'required', + Rule::dimensions()->maxWidth(1000)->maxHeight(500)->ratio(3 / 2), + ], + ]); + #### distinct @@ -644,23 +700,22 @@ The field under validation must exist on a given database table. 'state' => 'exists:states,abbreviation' -You may also specify more conditions that will be added as "where" clauses to the query: - - 'email' => 'exists:staff,email,account_id,1' - -These conditions may be negated using the `!` sign: - - 'email' => 'exists:staff,email,role,!admin' +Occasionally, you may need to specify a specific database connection to be used for the `exists` query. You can accomplish this by prepending the connection name to the table name using "dot" syntax: -You may also pass `NULL` or `NOT_NULL` to the "where" clause: + 'email' => 'exists:connection.staff,email' - 'email' => 'exists:staff,email,deleted_at,NULL' +If you would like to customize the query executed by the validation rule, you may use the `Rule` class to fluently define the rule. In this example, we'll also specify the validation rules as an array instead of using the `|` character to delimit them: - 'email' => 'exists:staff,email,deleted_at,NOT_NULL' + use Illuminate\Validation\Rule; -Occasionally, you may need to specify a specific database connection to be used for the `exists` query. You can accomplish this by prepending the connection name to the table name using "dot" syntax: - - 'email' => 'exists:connection.staff,email' + Validator::make($data, [ + 'email' => [ + 'required', + Rule::exists('staff')->where(function ($query) { + $query->where('account_id', 1); + }), + ], + ]); #### file @@ -680,7 +735,16 @@ The file under validation must be an image (jpeg, png, bmp, gif, or svg) #### in:_foo_,_bar_,... -The field under validation must be included in the given list of values. +The field under validation must be included in the given list of values. Since this rule often requires you to `implode` an array, the `Rule::in` method may be used to fluently construct the rule: + + use Illuminate\Validation\Rule; + + Validator::make($data, [ + 'zones' => [ + 'required', + Rule::in(['first-zone', 'second-zone']), + ], + ]); #### in_array:_anotherfield_ @@ -697,6 +761,14 @@ The field under validation must be an integer. The field under validation must be an IP address. +#### ipv4 + +The field under validation must be an IPv4 address. + +#### ipv6 + +The field under validation must be an IPv6 address. + #### json @@ -727,7 +799,7 @@ The file under validation must have a MIME type corresponding to one of the list Even though you only need to specify the extensions, this rule actually validates against the MIME type of the file by reading the file's contents and guessing its MIME type. -A full listing of MIME types and their corresponding extensions may be found at the following location: [http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types](http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types) +A full listing of MIME types and their corresponding extensions may be found at the following location: [https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types](https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types) #### min:_value_ @@ -842,21 +914,30 @@ Occasionally, you may need to set a custom connection for database queries made **Forcing A Unique Rule To Ignore A Given ID:** -Sometimes, you may wish to ignore a given ID during the unique check. For example, consider an "update profile" screen that includes the user's name, e-mail address, and location. Of course, you will want to verify that the e-mail address is unique. However, if the user only changes the name field and not the e-mail field, you do not want a validation error to be thrown because the user is already the owner of the e-mail address. To tell the unique rule to ignore the user's ID, you may pass the ID as the third parameter: +Sometimes, you may wish to ignore a given ID during the unique check. For example, consider an "update profile" screen that includes the user's name, e-mail address, and location. Of course, you will want to verify that the e-mail address is unique. However, if the user only changes the name field and not the e-mail field, you do not want a validation error to be thrown because the user is already the owner of the e-mail address. - 'email' => 'unique:users,email_address,'.$user->id +To instruct the validator to ignore the user's ID, we'll use the `Rule` class to fluently define the rule. In this example, we'll also specify the validation rules as an array instead of using the `|` character to delimit the rules: -If your table uses a primary key column name other than `id`, you may specify it as the fourth parameter: + use Illuminate\Validation\Rule; - 'email' => 'unique:users,email_address,'.$user->id.',user_id' + Validator::make($data, [ + 'email' => [ + 'required', + Rule::unique('users')->ignore($user->id), + ], + ]); -**Adding Additional Where Clauses:** +If your table uses a primary key column name other than `id`, you may specify the name of the column when calling the `ignore` method: + + 'email' => Rule::unique('users')->ignore($user->id, 'user_id') -You may also specify more conditions that will be added as "where" clauses to the query: +**Adding Additional Where Clauses:** - 'email' => 'unique:users,email_address,NULL,id,account_id,1' +You may also specify additional query constraints by customizing the query using the `where` method. For example, let's add a constraint that verifies the `account_id` is `1`: -In the rule above, only rows with an `account_id` of `1` would be included in the unique check. + 'email' => Rule::unique('users')->where(function ($query) { + $query->where('account_id', 1); + }) #### url @@ -887,13 +968,13 @@ Sometimes you may wish to add validation rules based on more complex conditional Let's assume our web application is for game collectors. If a game collector registers with our application and they own more than 100 games, we want them to explain why they own so many games. For example, perhaps they run a game resale shop, or maybe they just enjoy collecting. To conditionally add this requirement, we can use the `sometimes` method on the `Validator` instance. - $v->sometimes('reason', 'required|max:500', function($input) { + $v->sometimes('reason', 'required|max:500', function ($input) { return $input->games >= 100; }); The first argument passed to the `sometimes` method is the name of the field we are conditionally validating. The second argument is the rules we want to add. If the `Closure` passed as the third argument returns `true`, the rules will be added. This method makes it a breeze to build complex conditional validations. You may even add conditional validations for several fields at once: - $v->sometimes(['reason', 'cost'], 'required', function($input) { + $v->sometimes(['reason', 'cost'], 'required', function ($input) { return $input->games >= 100; }); @@ -938,7 +1019,7 @@ Laravel provides a variety of helpful validation rules; however, you may wish to */ public function boot() { - Validator::extend('foo', function($attribute, $value, $parameters, $validator) { + Validator::extend('foo', function ($attribute, $value, $parameters, $validator) { return $value == 'foo'; }); } @@ -981,7 +1062,7 @@ When creating a custom validation rule, you may sometimes need to define custom { Validator::extend(...); - Validator::replacer('foo', function($message, $attribute, $rule, $parameters) { + Validator::replacer('foo', function ($message, $attribute, $rule, $parameters) { return str_replace(...); }); } @@ -998,7 +1079,7 @@ By default, when an attribute being validated is not present or contains an empt For a rule to run even when an attribute is empty, the rule must imply that the attribute is required. To create such an "implicit" extension, use the `Validator::extendImplicit()` method: - Validator::extendImplicit('foo', function($attribute, $value, $parameters, $validator) { + Validator::extendImplicit('foo', function ($attribute, $value, $parameters, $validator) { return $value == 'foo'; }); diff --git a/views.md b/views.md index 6e7b177..80e2063 100644 --- a/views.md +++ b/views.md @@ -10,7 +10,7 @@ Views contain the HTML served by your application and separate your controller / application logic from your presentation logic. Views are stored in the `resources/views` directory. A simple view might look something like this: - + @@ -18,7 +18,7 @@ Views contain the HTML served by your application and separate your controller / -Since this view is stored at `resources/views/greeting.php`, we may return it using the global `view` helper like so: +Since this view is stored at `resources/views/greeting.blade.php`, we may return it using the global `view` helper like so: Route::get('/', function () { return view('greeting', ['name' => 'James']); @@ -26,7 +26,7 @@ Since this view is stored at `resources/views/greeting.php`, we may return it us As you can see, the first argument passed to the `view` helper corresponds to the name of the view file in the `resources/views` directory. The second argument is an array of data that should be made available to the view. In this case, we are passing the `name` variable, which is displayed in the view using [Blade syntax](/docs/{{version}}/blade). -Of course, views may also be nested within sub-directories of the `resources/views` directory. "Dot" notation may be used to reference nested views. For example, if your view is stored at `resources/views/admin/profile.php`, you may reference it like so: +Of course, views may also be nested within sub-directories of the `resources/views` directory. "Dot" notation may be used to reference nested views. For example, if your view is stored at `resources/views/admin/profile.blade.php`, you may reference it like so: return view('admin.profile', $data);