From f0e0f560a69f812bb3f52a128d0419b7f4200c23 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 2 Nov 2020 17:11:57 -0600 Subject: [PATCH 001/274] initial toc work --- documentation.md | 4 +--- installation.md | 15 ++++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/documentation.md b/documentation.md index d2b0f201cf..93eac638d9 100644 --- a/documentation.md +++ b/documentation.md @@ -4,13 +4,11 @@ - [Contribution Guide](/docs/{{version}}/contributions) - ## Getting Started - [Installation](/docs/{{version}}/installation) + - [Request Lifecycle](/docs/{{version}}/lifecycle) - [Configuration](/docs/{{version}}/configuration) - [Directory Structure](/docs/{{version}}/structure) - - [Homestead](/docs/{{version}}/homestead) - - [Valet](/docs/{{version}}/valet) - [Deployment](/docs/{{version}}/deployment) - ## Architecture Concepts - - [Request Lifecycle](/docs/{{version}}/lifecycle) - [Service Container](/docs/{{version}}/container) - [Service Providers](/docs/{{version}}/providers) - [Facades](/docs/{{version}}/facades) diff --git a/installation.md b/installation.md index 4e781a2a9e..a6990e31f3 100644 --- a/installation.md +++ b/installation.md @@ -1,12 +1,13 @@ # Installation -- [Installation](#installation) - - [Server Requirements](#server-requirements) - - [Installing Laravel](#installing-laravel) - - [Configuration](#configuration) -- [Web Server Configuration](#web-server-configuration) - - [Directory Configuration](#directory-configuration) - - [Pretty URLs](#pretty-urls) +- [Meet Laravel](#meet-laravel) +- [Your First Laravel Project](#installation) + - [Laravel Sail](#laravel-sail) + - [Installation For MacOS](#installation-for-macos) + - [Installation For Windows](#installation-for-windows) + - [Installation For Linux](#installation-for-linux) +- [Initial Configuration](#initial-configuration) +- [Next Steps](#next-steps) ## Installation From 3971eadd3d76ab81c5d5472e98ec19b6e73ac481 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 3 Nov 2020 14:45:55 -0600 Subject: [PATCH 002/274] more work on onboarding docs --- installation.md | 183 ++++++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 100 deletions(-) diff --git a/installation.md b/installation.md index a6990e31f3..1bd42b964c 100644 --- a/installation.md +++ b/installation.md @@ -1,154 +1,137 @@ # Installation - [Meet Laravel](#meet-laravel) -- [Your First Laravel Project](#installation) - - [Laravel Sail](#laravel-sail) - - [Installation For MacOS](#installation-for-macos) - - [Installation For Windows](#installation-for-windows) - - [Installation For Linux](#installation-for-linux) +- [Your First Laravel Project](#your-first-laravel-project) + - [Getting Started On MacOS](#getting-started-on-macos) + - [Getting Started On Windows](#getting-started-on-windows) + - [Getting Started On Linux](#getting-started-on-linux) - [Initial Configuration](#initial-configuration) +- [Laravel Sail](#laravel-sail) + - [Introduction](#laravel-sail-introduction) + - [Starting & Stopping](#starting-and-stopping-sail) + - [Executing Commands](#executing-commands) + - [Interacting With Databases](#interacting-with-databases) + - [Adding Additional Services](#adding-additional-services) + - [Container CLI](#container-cli) - [Next Steps](#next-steps) - -## Installation + +## Meet Laravel - -### Server Requirements +Laravel is a web application framework with expressive, elegant syntax. A web framework provides a structure and starting point for creating your application, allowing you to focus on creating something amazing while we sweat the details. -The Laravel framework has a few system requirements. All of these requirements are satisfied by the [Laravel Homestead](/docs/{{version}}/homestead) virtual machine, so it's highly recommended that you use Homestead as your local Laravel development environment. +Laravel strives to provide an amazing developer experience, while providing powerful features such as thorough dependency injection, an expressive database abstraction layer, queues and scheduled jobs, unit and integration testing, and more. -However, if you are not using Homestead, you will need to make sure your server meets the following requirements: +Whether your new to PHP or web frameworks or have years of experience, Laravel is a framework that can grow with you. We'll help you take your first steps as a web developer or give you a boost as you take your expertise to the next level. We can't wait to see what you build. -
-- PHP >= 7.3 -- BCMath PHP Extension -- Ctype PHP Extension -- Fileinfo PHP Extension -- JSON PHP Extension -- Mbstring PHP Extension -- OpenSSL PHP Extension -- PDO PHP Extension -- Tokenizer PHP Extension -- XML PHP Extension -
+ +## Your First Laravel Project - -### Installing Laravel +We want it to be as easy as possible to get started with Laravel. There are a variety of options for developing and running a Laravel project on your own computer. While you may wish to explore these options at a later time, Laravel provides Sail, a built-in solution for running your Laravel project using [Docker](https://www.docker.com). -Laravel utilizes [Composer](https://getcomposer.org) to manage its dependencies. So, before using Laravel, make sure you have Composer installed on your machine. +Docker is a tool for running applications and services in small, light-weight "containers" which do not interfere with your local computer's installed software or configuration. This means you don't have to worry about configuring or setting up complicated development tools such as web servers and databases on your personal computer. To get started, you only need to install [Docker Desktop](https://www.docker.com/products/docker-desktop). - -#### Via Laravel Installer +Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis. -First, download the Laravel installer using Composer: +> {tip} Already a Docker expert? Don't worry! Everything about Sail can be customized using the `docker-compose.yml` file included with Laravel. - composer global require laravel/installer + +### Getting Started On MacOS -Make sure to place Composer's system-wide vendor bin directory in your `$PATH` so the laravel executable can be located by your system. This directory exists in different locations based on your operating system; however, some common locations include: +If you're developing on a Mac and [Docker Desktop](https://www.docker.com/products/docker-desktop) is installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: -
-- macOS: `$HOME/.composer/vendor/bin` -- Windows: `%USERPROFILE%\AppData\Roaming\Composer\vendor\bin` -- GNU / Linux Distributions: `$HOME/.config/composer/vendor/bin` or `$HOME/.composer/vendor/bin` -
+```bash +curl -s https://laravel.build/my-app | bash +``` -You could also find the composer's global installation path by running `composer global about` and looking up from the first line. +Of course, you can change `my-app` in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. -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: +After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: - laravel new blog +```bash +cd my-app -> {tip} Want to create a Laravel project with login, registration, and more features already built for you? Check out [Laravel Jetstream](https://jetstream.laravel.com). +./sail up +``` - -#### Via Composer Create-Project +Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. To continue learning more about Laravel Sail, review its [complete documentation](#laravel-sail). -Alternatively, you may also install Laravel by issuing the Composer `create-project` command in your terminal: + +### Getting Started On Windows - composer create-project --prefer-dist laravel/laravel blog +Before we create a new Laravel application on your Windows machine, make sure to install [Docker Desktop](https://www.docker.com/products/docker-desktop). Next, you should ensure that Windows Subsystem for Linux 2 (WSL2) is installed and enabled. WSL allows you to run Linux binary executables natively on Windows 10. Information on how to install and enable WSL2 can be found within Microsoft's [developer environment documentation](https://docs.microsoft.com/en-us/windows/wsl/install-win10). - -#### Local Development Server +> {tip} After installing and enabling WSL2, you should ensure that Docker Desktop is [configured to use the WSL2 backend](https://docs.docker.com/docker-for-windows/wsl/). -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`: +Next, you are ready to create your first Laravel project. Launch [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) and begin a new terminal session for your WSL2 Linux operating system. Next, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: - php artisan serve +```bash +curl -s https://laravel.build/my-app | bash +``` -More robust local development options are available via [Homestead](/docs/{{version}}/homestead) and [Valet](/docs/{{version}}/valet). +Of course, you can change `my-app` in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. - -### Configuration +After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: - -#### Public Directory +```bash +cd my-app -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. +./sail up +``` - -#### Configuration Files +Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. To continue learning more about Laravel Sail, review its [complete documentation](#laravel-sail). -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. +#### Developing Within WSL2 - -#### Directory Permissions +Of course, you will need to be able to modify the Laravel application files that were created within your WSL2 installation. To accomplish this, we recommend using Microsoft's [Visual Studio Code](https://code.visualstudio.com) editor and their first-party extension for [Remote Development](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack). -After installing Laravel, you may need to configure some permissions. Directories within the `storage` and the `bootstrap/cache` directories should be writable by your web server or Laravel will not run. If you are using the [Homestead](/docs/{{version}}/homestead) virtual machine, these permissions should already be set. + +### Getting Started On Linux - -#### Application Key +If you're developing on Linux and [Docker](https://www.docker.com) is installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: -The next thing you should do after installing Laravel is set your application key to a random string. If you installed Laravel via Composer or the Laravel installer, this key has already been set for you by the `php artisan key:generate` command. +```bash +curl -s https://laravel.build/my-app | bash +``` -Typically, this string should be 32 characters long. The key can be set in the `.env` environment file. If you have not copied the `.env.example` file to a new file named `.env`, you should do that now. **If the application key is not set, your user sessions and other encrypted data will not be secure!** +Of course, you can change `my-app` in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. - -#### Additional Configuration +After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: -Laravel needs almost no other configuration out of the box. You are free to get started developing! However, you may wish to review the `config/app.php` file and its documentation. It contains several options such as `timezone` and `locale` that you may wish to change according to your application. - -You may also want to configure a few additional components of Laravel, such as: - -
-- [Cache](/docs/{{version}}/cache#configuration) -- [Database](/docs/{{version}}/database#configuration) -- [Session](/docs/{{version}}/session#configuration) -
+```bash +cd my-app - -## Web Server Configuration +./sail up +``` - -### Directory Configuration +Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. To continue learning more about Laravel Sail, review its [complete documentation](#laravel-sail). -Laravel should always be served out of the root of the "web directory" configured for your web server. You should not attempt to serve a Laravel application out of a subdirectory of the "web directory". Attempting to do so could expose sensitive files present within your application. + +### Installation Via Composer - -### Pretty URLs +If your local computer already has PHP and Composer installed, you may create a new Laravel project by using Composer directly. After the application has been created, you may start Laravel's local development server using the Artisan CLI's `serve` command: - -#### Apache + composer create-project laravel/laravel my-app -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. + cd my-app -If the `.htaccess` file that ships with Laravel does not work with your Apache installation, try this alternative: + php artisan serve - Options +FollowSymLinks -Indexes - RewriteEngine On + +## Initial Configuration - RewriteCond %{HTTP:Authorization} . - RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +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. - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^ index.php [L] +However, Laravel needs almost no additional configuration out of the box. You are free to get started developing! However, you may wish to review the `config/app.php` file and its documentation. It contains several options such as `timezone` and `locale` that you may wish to change according to your application. - -#### Nginx +You may also want to configure a few additional components of Laravel, such as: -If you are using Nginx, the following directive in your site configuration will direct all requests to the `index.php` front controller: +
+- [Cache](/docs/{{version}}/cache#configuration) +- [Database](/docs/{{version}}/database#configuration) +- [Session](/docs/{{version}}/session#configuration) +
- location / { - try_files $uri $uri/ /index.php?$query_string; - } + +## Laravel Sail -When using [Homestead](/docs/{{version}}/homestead) or [Valet](/docs/{{version}}/valet), pretty URLs will be automatically configured. From 60e305c87935cbe8b075f1c5b9263af824cce15f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 3 Nov 2020 15:24:33 -0600 Subject: [PATCH 003/274] beginning sail docs --- installation.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/installation.md b/installation.md index 1bd42b964c..6b9d49e07b 100644 --- a/installation.md +++ b/installation.md @@ -31,14 +31,14 @@ We want it to be as easy as possible to get started with Laravel. There are a va Docker is a tool for running applications and services in small, light-weight "containers" which do not interfere with your local computer's installed software or configuration. This means you don't have to worry about configuring or setting up complicated development tools such as web servers and databases on your personal computer. To get started, you only need to install [Docker Desktop](https://www.docker.com/products/docker-desktop). -Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis. +Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience. > {tip} Already a Docker expert? Don't worry! Everything about Sail can be customized using the `docker-compose.yml` file included with Laravel. ### Getting Started On MacOS -If you're developing on a Mac and [Docker Desktop](https://www.docker.com/products/docker-desktop) is installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: +If you're developing on a Mac and [Docker Desktop](https://www.docker.com/products/docker-desktop) is already installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: ```bash curl -s https://laravel.build/my-app | bash @@ -85,10 +85,12 @@ Once the application's Docker containers have been started, you can access the a Of course, you will need to be able to modify the Laravel application files that were created within your WSL2 installation. To accomplish this, we recommend using Microsoft's [Visual Studio Code](https://code.visualstudio.com) editor and their first-party extension for [Remote Development](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack). +Once these tools are installed, you may open any Laravel project by executing the `code .` command from your application's root directory using Windows Terminal. + ### Getting Started On Linux -If you're developing on Linux and [Docker](https://www.docker.com) is installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: +If you're developing on Linux and [Docker](https://www.docker.com) is already installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: ```bash curl -s https://laravel.build/my-app | bash @@ -109,7 +111,7 @@ Once the application's Docker containers have been started, you can access the a ### Installation Via Composer -If your local computer already has PHP and Composer installed, you may create a new Laravel project by using Composer directly. After the application has been created, you may start Laravel's local development server using the Artisan CLI's `serve` command: +If your computer already has PHP and Composer installed, you may create a new Laravel project by using Composer directly. After the application has been created, you may start Laravel's local development server using the Artisan CLI's `serve` command: composer create-project laravel/laravel my-app @@ -129,9 +131,16 @@ You may also want to configure a few additional components of Laravel, such as:
- [Cache](/docs/{{version}}/cache#configuration) - [Database](/docs/{{version}}/database#configuration) +- [Queues](/docs/{{version}}/queues#introduction) - [Session](/docs/{{version}}/session#configuration)
## Laravel Sail + +### Introduction + +Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience. + +At its heart, Sail is the `docker-compose.yml` file and the `sail` script that is stored at the root of your project. The `sail` script provides a CLI with convenient methods for interacting with the Docker containers defined by the `docker-compose.yml` file. From 3fe558f152781aecebb02a6d0173e422782d6b89 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 3 Nov 2020 15:42:28 -0600 Subject: [PATCH 004/274] continuing sail documentation --- installation.md | 51 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/installation.md b/installation.md index 6b9d49e07b..5bf0daf8b1 100644 --- a/installation.md +++ b/installation.md @@ -9,10 +9,10 @@ - [Laravel Sail](#laravel-sail) - [Introduction](#laravel-sail-introduction) - [Starting & Stopping](#starting-and-stopping-sail) - - [Executing Commands](#executing-commands) - - [Interacting With Databases](#interacting-with-databases) - - [Adding Additional Services](#adding-additional-services) - - [Container CLI](#container-cli) + - [Executing Commands](#executing-sail-commands) + - [Interacting With Databases](#interacting-with-sail-databases) + - [Adding Additional Services](#adding-additional-sail-services) + - [Container CLI](#sail-container-cli) - [Next Steps](#next-steps) @@ -144,3 +144,46 @@ You may also want to configure a few additional components of Laravel, such as: Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker configuration. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience. At its heart, Sail is the `docker-compose.yml` file and the `sail` script that is stored at the root of your project. The `sail` script provides a CLI with convenient methods for interacting with the Docker containers defined by the `docker-compose.yml` file. + + +### Starting & Stopping + +To start all of the Docker containers defined in your `docker-compose.yml` file, you may run the `up` command: + +```bash +./sail up +``` + +Once the application's containers have been started, you may access the project in your web browser at: http://localhost. + +To stop all of the containers, you may simply use Control + C to stop the container's execution. Or, if the containers are running in the background, you may use the `down` command: + +```bash +./sail down +``` + + +### Executing Commands + +When using Laravel Sail, your application is executing within a Docker container and is isolated from your local computer. However, Sail provides a convenient way to run various commands against your application, such as arbitrary PHP commands, Artisan commands, Composer commands, and NPM / Node commands: + +```bash +./sail php --version + +./sail artisan queue:work + +./sail composer require laravel/sanctum + +./sail node --version + +./sail npm run prod +``` + + +### Interacting With Databases + + +### Adding Additional Services + + +### Container CLI From 66f16be20d202411562bb5cf99ea8af3e17ef216 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 3 Nov 2020 16:32:18 -0600 Subject: [PATCH 005/274] more onboarding docs --- installation.md | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/installation.md b/installation.md index 5bf0daf8b1..cf46e6491a 100644 --- a/installation.md +++ b/installation.md @@ -13,6 +13,7 @@ - [Interacting With Databases](#interacting-with-sail-databases) - [Adding Additional Services](#adding-additional-sail-services) - [Container CLI](#sail-container-cli) + - [Customization](#sail-customization) - [Next Steps](#next-steps) @@ -124,9 +125,13 @@ If your computer already has PHP and Composer installed, you may create a new La 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. -However, Laravel needs almost no additional configuration out of the box. You are free to get started developing! However, you may wish to review the `config/app.php` file and its documentation. It contains several options such as `timezone` and `locale` that you may wish to change according to your application. +Laravel needs almost no additional configuration out of the box. You are free to get started developing! However, you may wish to review the `config/app.php` file and its documentation. It contains several options such as `timezone` and `locale` that you may wish to change according to your application. -You may also want to configure a few additional components of Laravel, such as: +In addition, since many of Laravel's configuration option values may vary depending on whether your application is running on your local computer or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. + +Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. + +After examining Laravel's basic configuration options, you may wish to configure a few additional components of Laravel, such as:
- [Cache](/docs/{{version}}/cache#configuration) @@ -182,8 +187,43 @@ When using Laravel Sail, your application is executing within a Docker container ### Interacting With Databases + +#### MySQL + +As you may have noticed, your application's `docker-compose.yml` file contains an entry for a MySQL container. This container uses a [Docker volume](https://docs.docker.com/storage/volumes/) so that the data stored in your database is persisted even when stopping and restarting your containers. Once you have started your containers, you may connect to the MySQL instance within your application by setting your `DB_HOST` environment variable within your application's `.env` file to `mysql`. + +To connect to your application's MySQL database from your local machine, you may use a graphical database management application such as [TablePlus](https://tableplus.com). By default, the MySQL database is accessible at `localhost` port `3306`. + + +#### Redis + +Your application's `docker-compose.yml` file also contains an entry for a [Redis](https://redis.io) container. This container uses a [Docker volume](https://docs.docker.com/storage/volumes/) so that the data stored in your Redis data is persisted even when stopping and restarting your containers. Once you have started your containers, you may connect to the Redis instance within your application by setting your `REDIS_HOST` environment variable within your application's `.env` file to `redis`. + +To connect to your application's Redis database from your local machine, you may use a graphical database management application such as [TablePlus](https://tableplus.com). By default, the Redis database is accessible at `localhost` port `6379`. + ### Adding Additional Services ### Container CLI + +Sometimes you may wish to start a Bash session within your application's container. You may use the `ssh` command to connect to your application's container, allowing you to inspect its file and installed services: + +```bash +./sail ssh +``` + + +### Sail Customization + +Since Sail is just Docker, you are free to customize nearly everything about it. To publish Sail's own Dockerfiles, you may execute the `sail:install` Artisan command and publish the resources exported by the `Laravel\Sail\SailServiceProvider` service provider: + +```bash +./sail artisan sail:install +``` + +After running this command, the Dockerfiles and other configuration files used by Laravel Sail will be placed within a `docker` directory in your application's root directory. After customizing your Sail installation, you may rebuild your application's containers using the `build` command: + +```bash +./sail build +``` From 1576112feb7d31c7722e7aaf50c1865566513c1c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 3 Nov 2020 16:44:16 -0600 Subject: [PATCH 006/274] wip --- installation.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/installation.md b/installation.md index cf46e6491a..a5cffabbc5 100644 --- a/installation.md +++ b/installation.md @@ -127,18 +127,14 @@ All of the configuration files for the Laravel framework are stored in the `conf Laravel needs almost no additional configuration out of the box. You are free to get started developing! However, you may wish to review the `config/app.php` file and its documentation. It contains several options such as `timezone` and `locale` that you may wish to change according to your application. -In addition, since many of Laravel's configuration option values may vary depending on whether your application is running on your local computer or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. + +#### Environment Based Configuration -Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. +Since many of Laravel's configuration option values may vary depending on whether your application is running on your local computer or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. -After examining Laravel's basic configuration options, you may wish to configure a few additional components of Laravel, such as: +Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. -
-- [Cache](/docs/{{version}}/cache#configuration) -- [Database](/docs/{{version}}/database#configuration) -- [Queues](/docs/{{version}}/queues#introduction) -- [Session](/docs/{{version}}/session#configuration) -
+> {tip} For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). ## Laravel Sail @@ -227,3 +223,6 @@ After running this command, the Dockerfiles and other configuration files used b ```bash ./sail build ``` + + +## Next Steps From 5c557faabc3d007d76f77d55a2e0ecf01ed1251c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 5 Nov 2020 11:00:31 -0600 Subject: [PATCH 007/274] move valet and homestead docs --- documentation.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation.md b/documentation.md index 93eac638d9..1e1ac4f4bc 100644 --- a/documentation.md +++ b/documentation.md @@ -78,6 +78,7 @@ - [Cashier (Mollie)](https://github.com/laravel/cashier-mollie) - [Dusk](/docs/{{version}}/dusk) - [Envoy](/docs/{{version}}/envoy) + - [Homestead](/docs/{{version}}/homestead) - [Horizon](/docs/{{version}}/horizon) - [Jetstream](https://jetstream.laravel.com) - [Passport](/docs/{{version}}/passport) @@ -85,4 +86,5 @@ - [Scout](/docs/{{version}}/scout) - [Socialite](/docs/{{version}}/socialite) - [Telescope](/docs/{{version}}/telescope) + - [Valet](/docs/{{version}}/valet) - [API Documentation](/api/8.x) From 91deeea1f3c28f36964fa8c15a4b02c13f3e5fd0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 5 Nov 2020 13:55:16 -0600 Subject: [PATCH 008/274] basic next steps and more docs --- installation.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/installation.md b/installation.md index a5cffabbc5..d6b6399457 100644 --- a/installation.md +++ b/installation.md @@ -5,6 +5,7 @@ - [Getting Started On MacOS](#getting-started-on-macos) - [Getting Started On Windows](#getting-started-on-windows) - [Getting Started On Linux](#getting-started-on-linux) + - [Installation Via Composer](#installation-via-composer) - [Initial Configuration](#initial-configuration) - [Laravel Sail](#laravel-sail) - [Introduction](#laravel-sail-introduction) @@ -28,7 +29,7 @@ Whether your new to PHP or web frameworks or have years of experience, Laravel i ## Your First Laravel Project -We want it to be as easy as possible to get started with Laravel. There are a variety of options for developing and running a Laravel project on your own computer. While you may wish to explore these options at a later time, Laravel provides Sail, a built-in solution for running your Laravel project using [Docker](https://www.docker.com). +We want it to be as easy as possible to get started with Laravel. There are a variety of options for developing and running a Laravel project on your own computer. While you may wish to explore these options at a later time, Laravel provides [Sail](https://github.com/laravel/sail), a built-in solution for running your Laravel project using [Docker](https://www.docker.com). Docker is a tool for running applications and services in small, light-weight "containers" which do not interfere with your local computer's installed software or configuration. This means you don't have to worry about configuring or setting up complicated development tools such as web servers and databases on your personal computer. To get started, you only need to install [Docker Desktop](https://www.docker.com/products/docker-desktop). @@ -200,6 +201,27 @@ To connect to your application's Redis database from your local machine, you may ### Adding Additional Services +Since Laravel Sail is built using a standard `docker-compose.yml` file, you are free to add additional services to your container configuration based on your application's own needs. + + +#### Sharing Services Across Projects + +Sometimes you may wish to share a single service such as MySQL across multiple projects. To accomplish this, we recommend using [Takeout](https://github.com/tighten/takeout), an open source tool developed by Tighten. After enabling a Takeout service, you should add the `takeout` network to your `laravel.test` container definition within your `docker-compose.yml` file: + +```yaml +networks: + - sail + - takeout +``` + +Once you have added your container to the `takeout` network, you may start Sail: + +```bash +./sail up +``` + +For more information on using Takeout services and connecting them to Sail, please consult the [official Takeout documentation](https://github.com/tighten/takeout). + ### Container CLI @@ -226,3 +248,5 @@ After running this command, the Dockerfiles and other configuration files used b ## Next Steps + +Now that you have created your Laravel project, you may wondering what to learn next. We recommend becoming familiar with how Laravel works by reading our documentation on the Laravel [request lifecycle](/docs/{{version}}/lifecycle). Or, if you want to jump straight into coding, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). From 4208ec2d7af21c960134081f3cdd6c26e3a0f5f3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 5 Nov 2020 15:29:04 -0600 Subject: [PATCH 009/274] onboarding commits --- configuration.md | 61 +++++++++++++++++++----------------------------- errors.md | 2 +- installation.md | 2 +- 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/configuration.md b/configuration.md index 6c7a2f2c52..e5f519e967 100644 --- a/configuration.md +++ b/configuration.md @@ -8,6 +8,7 @@ - [Hiding Environment Variables From Debug Pages](#hiding-environment-variables-from-debug) - [Accessing Configuration Values](#accessing-configuration-values) - [Configuration Caching](#configuration-caching) +- [Debug Mode](#debug-mode) - [Maintenance Mode](#maintenance-mode) @@ -15,23 +16,30 @@ 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. +These configuration files allow you to configure things like your database connection information, your mail server information, as well as various other core configuration values such as your application timezone and encryption key. + ## Environment Configuration It is often helpful to have different configuration values based on the environment where the application is running. For example, you may wish to use a different cache driver locally than you do on your production server. -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 copied to `.env`. Otherwise, you should copy the file manually. +To make this a cinch, Laravel utilizes the [DotEnv](https://github.com/vlucas/phpdotenv) PHP library. In a fresh Laravel installation, the root directory of your application will contain a `.env.example` file. During the Laravel installation process, this file will automatically be copied to `.env`. -Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. +Laravel's default `.env` file contains some common configuration values that may differ based on whether your application is running locally or on a production web server. These values are then retrieved from various Laravel configuration files within the `config` directory using Laravel's `env` function. -If you are developing with a team, you may wish to continue including a `.env.example` file with your application. By putting placeholder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application. You may also create a `.env.testing` file. This file will override the `.env` file when running PHPUnit tests or executing Artisan commands with the `--env=testing` option. +If you are developing with a team, you may wish to continue including a `.env.example` file with your application. By putting placeholder values in the example configuration file, other developers on your team can clearly see which environment variables are needed to run your application. > {tip} Any variable in your `.env` file can be overridden by external environment variables such as server-level or system-level environment variables. + +#### Environment File Security + +Your `.env` file should not be committed to your application's source control, since each developer / server using your application could require a different environment configuration. Furthermore, this would be a security risk in the event an intruder gains access to your source control repository, since any sensitive credentials would get exposed. + ### Environment Variable Types -All variables in your `.env` files are parsed as strings, so some reserved values have been created to allow you to return a wider range of types from the `env()` function: +All variables in your `.env` files are typically parsed as strings, so some reserved values have been created to allow you to return a wider range of types from the `env()` function: `.env` Value | `env()` Value ------------- | ------------- @@ -64,7 +72,7 @@ The current application environment is determined via the `APP_ENV` variable fro $environment = App::environment(); -You may also pass arguments to the `environment` method to check if the environment matches a given value. The method will return `true` if the environment matches any of the given values: +You may also pass arguments to the `environment` method to determine if the environment matches a given value. The method will return `true` if the environment matches any of the given values: if (App::environment('local')) { // The environment is local @@ -74,35 +82,7 @@ You may also pass arguments to the `environment` method to check if the environm // The environment is either local OR staging... } -> {tip} The current application environment detection can be overridden by a server-level `APP_ENV` environment variable. This can be useful when you need to share the same application for different environment configurations, so you can set up a given host to match a given environment in your server's configurations. - - -### Hiding Environment Variables From Debug Pages - -When an exception is uncaught and the `APP_DEBUG` environment variable is `true`, the debug page will show all environment variables and their contents. In some cases you may want to obscure certain variables. You may do this by updating the `debug_hide` option in your `config/app.php` configuration file. - -Some variables are available in both the environment variables and the server / request data. Therefore, you may need to hide them for both `$_ENV` and `$_SERVER`: - - return [ - - // ... - - 'debug_hide' => [ - '_ENV' => [ - 'APP_KEY', - 'DB_PASSWORD', - ], - - '_SERVER' => [ - 'APP_KEY', - 'DB_PASSWORD', - ], - - '_POST' => [ - 'password', - ], - ], - ]; +> {tip} The current application environment detection can be overridden by defining a server-level `APP_ENV` environment variable. ## Accessing Configuration Values @@ -121,12 +101,19 @@ To set configuration values at runtime, pass an array to the `config` helper: ## Configuration Caching -To give your application a speed boost, you should cache all of your configuration files into a single file using the `config:cache` Artisan command. This will combine all of the configuration options for your application into a single file which will be loaded quickly by the framework. +To give your application a speed boost, you should cache all of your configuration files into a single file using the `config:cache` Artisan command. This will combine all of the configuration options for your application into a single file which can be quickly loaded by the framework. -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. +You should typically run the `php artisan config:cache` command as part of your production deployment process. 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. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function will return `null`. + +## Debug Mode + +The `debug` option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. + +For local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the value is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** + ## Maintenance Mode @@ -186,4 +173,4 @@ While your application is in maintenance mode, no [queued jobs](/docs/{{version} #### Alternatives To Maintenance Mode -Since maintenance mode requires your application to have several seconds of downtime, consider alternatives like [Envoyer](https://envoyer.io) to accomplish zero-downtime deployment with Laravel. +Since maintenance mode requires your application to have several seconds of downtime, consider alternatives like [Laravel Vapor](https://vapor.laravel.com) and [Envoyer](https://envoyer.io) to accomplish zero-downtime deployment with Laravel. diff --git a/errors.md b/errors.md index 1b4d3917ab..a924f63349 100644 --- a/errors.md +++ b/errors.md @@ -19,7 +19,7 @@ When you start a new Laravel project, error and exception handling is already co The `debug` option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. -For local development, you should set the `APP_DEBUG` environment variable to `true`. In your production environment, this value should always be `false`. If the value is set to `true` in production, you risk exposing sensitive configuration values to your application's end users. +For local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the value is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** ## The Exception Handler diff --git a/installation.md b/installation.md index d6b6399457..e9c518f9dc 100644 --- a/installation.md +++ b/installation.md @@ -249,4 +249,4 @@ After running this command, the Dockerfiles and other configuration files used b ## Next Steps -Now that you have created your Laravel project, you may wondering what to learn next. We recommend becoming familiar with how Laravel works by reading our documentation on the Laravel [request lifecycle](/docs/{{version}}/lifecycle). Or, if you want to jump straight into coding, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). +Now that you have created your Laravel project, you may wondering what to learn next. We recommend becoming familiar with how Laravel works by reading our documentation on Laravel's [request lifecycle](/docs/{{version}}/lifecycle), [configuration](/docs/{{version}}/configuration), or [directory structure](/docs/{{version}}/structure). Or, if you want to jump straight into coding, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). From f9b4739d3a0f974a0b2886ff5aff400455ba1728 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 5 Nov 2020 15:44:52 -0600 Subject: [PATCH 010/274] why laravel --- installation.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/installation.md b/installation.md index e9c518f9dc..b0fa202824 100644 --- a/installation.md +++ b/installation.md @@ -1,6 +1,7 @@ # Installation - [Meet Laravel](#meet-laravel) + - [Why Laravel?](#why-laravel) - [Your First Laravel Project](#your-first-laravel-project) - [Getting Started On MacOS](#getting-started-on-macos) - [Getting Started On Windows](#getting-started-on-windows) @@ -26,6 +27,27 @@ Laravel strives to provide an amazing developer experience, while providing powe Whether your new to PHP or web frameworks or have years of experience, Laravel is a framework that can grow with you. We'll help you take your first steps as a web developer or give you a boost as you take your expertise to the next level. We can't wait to see what you build. + +### Why Laravel? + +There are a variety of tools and frameworks available to you when building a web application. However, we believe Laravel is the best choice for building modern, full-stack web applications. + +#### A Progressive Framework + +We like to call Laravel a "progressive" framework. By that, we mean that Laravel grows with you. If you're just taking your first steps into web development, Laravel's vast library of documentation, guides, and [video tutorials](https://laracasts.com) will help you learn the ropes without becoming overwhelmed. + +If you're a senior developer, Laravel gives you robust tools for [dependency injection](/docs/{{version}}/container), [unit testing](/docs/{{version}}/testing), [queues](/docs/{{version}}/queues), [real-time events](/docs/{{version}}/broadcasting), and more. Laravel is fine-tuned for building professional web applications and ready to handle enterprise work loads. + +#### A Scalable Framework + +Laravel is incredibly scalable. Thanks to the scaling-friendly nature of PHP and Laravel's built-in support for fast, distributed cache systems like Redis, horizontal scaling with Laravel is a breeze. In fact, Laravel applications have been easily scaled to handle hundreds of millions of requests per month. + +Need extreme scaling? Platforms like [Laravel Vapor](https://vapor.laravel.com) allow you to run your Laravel application at nearly limitless scale on AWS's latest serverless technology. + +#### A Community Framework + +Laravel combines the best packages in the PHP ecosystem to offer the most robust and developer friendly framework available. In addition, thousands of talented developers from around the world have [contributed to the framework](https://github.com/laravel/framework). Who knows, maybe you'll even become a Laravel contributor. + ## Your First Laravel Project From 10950704cefce16180de14035531d10358d93a77 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 5 Nov 2020 15:57:28 -0600 Subject: [PATCH 011/274] update to the structure documentation --- structure.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/structure.md b/structure.md index eb10bf9507..553f4954cc 100644 --- a/structure.md +++ b/structure.md @@ -43,7 +43,7 @@ The `app` directory contains the core code of your application. We'll explore th #### The Bootstrap Directory -The `bootstrap` directory contains the `app.php` file which bootstraps the framework. This directory also houses a `cache` directory which contains framework generated files for performance optimization such as the route and services cache files. +The `bootstrap` directory contains the `app.php` file which bootstraps the framework. This directory also houses a `cache` directory which contains framework generated files for performance optimization such as the route and services cache files. You should not typically need to modify any files within this directory. #### The Config Directory @@ -63,32 +63,32 @@ The `public` directory contains the `index.php` file, which is the entry point f #### The Resources Directory -The `resources` directory contains your views as well as your raw, un-compiled assets such as CSS or JavaScript. This directory also houses all of your language files. +The `resources` directory contains your [views](/docs/{{version}}/views) as well as your raw, un-compiled assets such as CSS or JavaScript. This directory also houses all of your language files. #### The Routes Directory -The `routes` directory contains all of the route definitions for your application. By default, several route files are included with Laravel: `web.php`, `api.php`, `console.php` and `channels.php`. +The `routes` directory contains all of the route definitions for your application. By default, several route files are included with Laravel: `web.php`, `api.php`, `console.php`, and `channels.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 `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 then it is likely that 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 `api.php` file contains routes that the `RouteServiceProvider` places in the `api` middleware group. These routes are intended to be stateless, so requests entering the application through these routes are intended to be authenticated [via tokens](/docs/{{version}}/sanctum) 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 `channels.php` file is where you may register all of the event broadcasting channels that your application supports. +The `channels.php` file is where you may register all of the [event broadcasting](/docs/{{version}}/broadcasting) channels that your application supports. #### The Storage Directory -The `storage` directory contains your compiled Blade templates, file based sessions, file caches, and other files generated by the framework. This directory is segregated into `app`, `framework`, and `logs` directories. The `app` directory may be used to store any files generated by your application. The `framework` directory is used to store framework generated files and caches. Finally, the `logs` directory contains your application's log files. +The `storage` directory contains your logs, compiled Blade templates, file based sessions, file caches, and other files generated by the framework. This directory is segregated into `app`, `framework`, and `logs` directories. The `app` directory may be used to store any files generated by your application. The `framework` directory is used to store framework generated files and caches. Finally, the `logs` directory contains your application's log files. -The `storage/app/public` directory may be used to store user-generated files, such as profile avatars, that should be publicly accessible. You should create a symbolic link at `public/storage` which points to this directory. You may create the link using the `php artisan storage:link` command. +The `storage/app/public` directory may be used to store user-generated files, such as profile avatars, that should be publicly accessible. You should create a symbolic link at `public/storage` which points to this directory. You may create the link using the `php artisan storage:link` Artisan command. #### The Tests Directory -The `tests` directory contains your automated tests. An example [PHPUnit](https://phpunit.de/) test is provided out of the box. Each test class should be suffixed with the word `Test`. You may run your tests using the `phpunit` or `php vendor/bin/phpunit` commands. +The `tests` directory contains your automated tests. Example [PHPUnit](https://phpunit.de/) unit tests and feature tests are provided out of the box. Each test class should be suffixed with the word `Test`. You may run your tests using the `phpunit` or `php vendor/bin/phpunit` commands. Or, if you would like a more detailed and beautiful representation of your test results, you may run your tests using the `php artisan test` Artisan command. #### The Vendor Directory @@ -144,22 +144,22 @@ This directory does not exist by default, but will be created for you if you exe #### The Mail Directory -This directory does not exist by default, but will be created for you if you execute the `make:mail` Artisan command. The `Mail` directory contains all of your classes that represent emails sent by your application. Mail objects allow you to encapsulate all of the logic of building an email in a single, simple class that may be sent using the `Mail::send` method. +This directory does not exist by default, but will be created for you if you execute the `make:mail` Artisan command. The `Mail` directory contains all of your [classes that represent emails](/docs/{{version}}/mail) sent by your application. Mail objects allow you to encapsulate all of the logic of building an email in a single, simple class that may be sent using the `Mail::send` method. #### The Models Directory -The `Models` directory contains all of your Eloquent model classes. The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table. +The `Models` directory contains all of your [Eloquent model classes](/docs/{{version}}/eloquent). The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table. #### The Notifications Directory -This directory does not exist by default, but will be created for you if you execute the `make:notification` Artisan command. The `Notifications` directory contains all of the "transactional" notifications that are sent by your application, such as simple notifications about events that happen within your application. Laravel's notification features abstracts sending notifications over a variety of drivers such as email, Slack, SMS, or stored in a database. +This directory does not exist by default, but will be created for you if you execute the `make:notification` Artisan command. The `Notifications` directory contains all of the "transactional" [notifications](/docs/{{version}}/notifications) that are sent by your application, such as simple notifications about events that happen within your application. Laravel's notification features abstracts sending notifications over a variety of drivers such as email, Slack, SMS, or stored in a database. #### The Policies Directory -This directory does not exist by default, but will be created for you if you execute the `make:policy` Artisan command. The `Policies` directory contains the authorization policy classes for your application. Policies are used to determine if a user can perform a given action against a resource. For more information, check out the [authorization documentation](/docs/{{version}}/authorization). +This directory does not exist by default, but will be created for you if you execute the `make:policy` Artisan command. The `Policies` directory contains the [authorization policy classes](/docs/{{version}}/authorization) for your application. Policies are used to determine if a user can perform a given action against a resource. #### The Providers Directory From b960aff6303690d953a6ec779b9df5deac79b886 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 6 Nov 2020 08:22:15 -0600 Subject: [PATCH 012/274] work on lifecycle docs --- lifecycle.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lifecycle.md b/lifecycle.md index 43c2a47c9e..9ff6fbcd5b 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -2,6 +2,11 @@ - [Introduction](#introduction) - [Lifecycle Overview](#lifecycle-overview) + - [First Steps](#first-steps) + - [HTTP / Console Kernels](#http-console-kernels) + - [Service Providers](#service-providers) + - [Routing](#routing) + - [Finishing Up](#finishing-up) - [Focus On Service Providers](#focus-on-service-providers) @@ -14,8 +19,8 @@ The goal of this document is to give you a good, high-level overview of how the ## Lifecycle Overview - -### First Things + +### First Steps The entry point for all requests to a Laravel application is the `public/index.php` file. All requests are directed to this file by your web server (Apache / Nginx) configuration. The `index.php` file doesn't contain much code. Rather, it is a starting point for loading the rest of the framework. @@ -26,21 +31,21 @@ 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}}/configuration#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. Typically, this classes handle internal Laravel configuration that you do not need to worry about. -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 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. We'll talk more about these soon. -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. +The method signature for the HTTP kernel's `handle` method is quite simple: it receives a `Request` and returns 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. -#### Service Providers +### Service Providers -One of the most important Kernel bootstrapping actions is loading the [service providers](/docs/{{version}}/providers) for your application. All of the service providers for the application are configured in the `config/app.php` configuration file's `providers` array. First, the `register` method will be called on all providers, then, once all providers have been registered, the `boot` method will be called. +One of the most important kernel bootstrapping actions is loading the [service providers](/docs/{{version}}/providers) for your application. All of the service providers for the application are configured in the `config/app.php` configuration file's `providers` array. First, the `register` method will be called on all providers, then, once all providers have been registered, the `boot` method will be called. Service providers are responsible for bootstrapping all of the framework's various components, such as the database, queue, validation, and routing components. Since they bootstrap and configure every feature offered by the framework, service providers are the most important aspect of the entire Laravel bootstrap process. - -#### Dispatch Request + +### Routing Once the application has been bootstrapped and all service providers have been registered, the `Request` will be handed off to the router for dispatching. The router will dispatch the request to a route or controller, as well as run any route specific middleware. From 49de4cd54cdff2f4cfc81ae9debd30eb89b2ebe9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 6 Nov 2020 08:59:17 -0600 Subject: [PATCH 013/274] wip --- lifecycle.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lifecycle.md b/lifecycle.md index 9ff6fbcd5b..713015d181 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -31,7 +31,7 @@ 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}}/configuration#environment-configuration), and perform other tasks that need to be done before the request is actually handled. Typically, this classes handle internal Laravel configuration that you do not need to worry about. +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. Typically, these classes handle internal Laravel configuration that you do not need to worry about. 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. We'll talk more about these soon. @@ -44,11 +44,17 @@ One of the most important kernel bootstrapping actions is loading the [service p Service providers are responsible for bootstrapping all of the framework's various components, such as the database, queue, validation, and routing components. Since they bootstrap and configure every feature offered by the framework, service providers are the most important aspect of the entire Laravel bootstrap process. +You may be wondering why the `register` method of every service provider is called before calling the `boot` method on any service providers. The answer is simple. By calling the `register` method of every service provider first, service providers may depend on every container binding being registered and available by the time the `boot` method is executed. + ### Routing Once the application has been bootstrapped and all service providers have been registered, the `Request` will be handed off to the router for dispatching. The router will dispatch the request to a route or controller, as well as run any route specific middleware. +Middleware provide a convenient mechanism for filtering or examining HTTP requests entering your application. For example, Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to the login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. Some middleware are assigned to all routes within the application, like those defined in the `$middleware` property of your HTTP kernel, while some are only assigned to specific routes or route groups. You can learn more about middleware by reading the complete [middleware documentation](/docs/{{version}}/middleware). + +If the request passes through all of the matched route's assigned middleware, the route or controller method will be executed and the response returned by the route or controller method will be sent to the browser. + ## Focus On Service Providers From 447eb7f52e02f5fee39a7494ff81f297f89c6cff Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 6 Nov 2020 09:06:53 -0600 Subject: [PATCH 014/274] wip --- lifecycle.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lifecycle.md b/lifecycle.md index 713015d181..b095404e0b 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -53,7 +53,14 @@ Once the application has been bootstrapped and all service providers have been r Middleware provide a convenient mechanism for filtering or examining HTTP requests entering your application. For example, Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to the login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. Some middleware are assigned to all routes within the application, like those defined in the `$middleware` property of your HTTP kernel, while some are only assigned to specific routes or route groups. You can learn more about middleware by reading the complete [middleware documentation](/docs/{{version}}/middleware). -If the request passes through all of the matched route's assigned middleware, the route or controller method will be executed and the response returned by the route or controller method will be sent to the browser. +If the request passes through all of the matched route's assigned middleware, the route or controller method will be executed and the response returned by the route or controller method will be sent back through the route's chain of middleware. + + +### Finishing Up + +Once the route or controller method returns a response, the response will travel back outward through the route's middleware, giving the application a chance to modify or examine the outgoing response. + +Finally, once the response travels back through the middleware, the HTTP kernel's `handle` method returns the response object and the `index.php` file calls the `send` method on the returned response. The `send` method sends the response content to the user's web browser. We've finished our journey through the entire Laravel request lifecycle! ## Focus On Service Providers From 36632286b40df2a27ece1a5072d6d4cc1f2a2c26 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 6 Nov 2020 09:07:57 -0600 Subject: [PATCH 015/274] wip --- lifecycle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lifecycle.md b/lifecycle.md index b095404e0b..67fd360cdf 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -69,4 +69,4 @@ Service providers are truly the key to bootstrapping a Laravel application. The Having a firm grasp of how a Laravel application is built and bootstrapped via service providers is very valuable. Your application's default service providers are stored in the `app/Providers` directory. -By default, the `AppServiceProvider` is fairly empty. This provider is a great place to add your application's own bootstrapping and service container bindings. For large applications, you may wish to create several service providers, each with a more granular type of bootstrapping. +By default, the `AppServiceProvider` is fairly empty. This provider is a great place to add your application's own bootstrapping and service container bindings. For large applications, you may wish to create several service providers, each with more granular bootstrapping for specific services used by your application. From 210f0aa2401754b32d70835540bdb38407ce53a1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 6 Nov 2020 09:21:01 -0600 Subject: [PATCH 016/274] wip --- configuration.md | 2 +- deployment.md | 31 ++++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/configuration.md b/configuration.md index e5f519e967..c8aabaaef6 100644 --- a/configuration.md +++ b/configuration.md @@ -112,7 +112,7 @@ You should typically run the `php artisan config:cache` command as part of your The `debug` option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. -For local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the value is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** +For local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** ## Maintenance Mode diff --git a/deployment.md b/deployment.md index f2f806e5fe..3e5f549982 100644 --- a/deployment.md +++ b/deployment.md @@ -1,6 +1,7 @@ # Deployment - [Introduction](#introduction) +- [Server Requirements](#server-requirements) - [Server Configuration](#server-configuration) - [Nginx](#nginx) - [Optimization](#optimization) @@ -8,6 +9,7 @@ - [Optimizing Configuration Loading](#optimizing-configuration-loading) - [Optimizing Route Loading](#optimizing-route-loading) - [Optimizing View Loading](#optimizing-view-loading) +- [Debug Mode](#debug-mode) - [Deploying With Forge / Vapor](#deploying-with-forge-or-vapor) @@ -15,13 +17,33 @@ When you're ready to deploy your Laravel application to production, there are some important things you can do to make sure your application is running as efficiently as possible. In this document, we'll cover some great starting points for making sure your Laravel application is deployed properly. + +## Server Requirements + +The Laravel framework has a few system requirements. You should ensure that your web server has the following minimum PHP version and extensions: + +
+- PHP >= 7.3 +- BCMath PHP Extension +- Ctype PHP Extension +- Fileinfo PHP Extension +- JSON PHP Extension +- Mbstring PHP Extension +- OpenSSL PHP Extension +- PDO PHP Extension +- Tokenizer PHP Extension +- XML PHP Extension +
+ ## Server Configuration ### Nginx -If you are deploying your application to a server that is running Nginx, you may use the following configuration file as a starting point for configuring your web server. Most likely, this file will need to be customized depending on your server's configuration. If you would like assistance in managing your server, consider using a service such as [Laravel Forge](https://forge.laravel.com): +If you are deploying your application to a server that is running Nginx, you may use the following configuration file as a starting point for configuring your web server. Most likely, this file will need to be customized depending on your server's configuration. **If you would like assistance in managing your server, consider using a first-party Laravel server management and deployment service such as [Laravel Forge](https://forge.laravel.com).** + +Please ensure, like the configuration below, your web server directs all requests to your application's `public/index.php` file. You should never attempt to move the `index.php` file to your project's root, as serving the application from the project root will expose many sensitive configuration files to the public Internet: server { listen 80; @@ -97,6 +119,13 @@ When deploying your application to production, you should make sure that you run This command precompiles all your Blade views so they are not compiled on demand, improving the performance of each request that returns a view. + +## Debug Mode + +The debug option in your config/app.php configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the APP_DEBUG environment variable, which is stored in your .env file. + +**In your production environment, this value should always be `false`. If the `APP_DEBUG` variable is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** + ## Deploying With Forge / Vapor From c414893350483acd58405d0d01cc4cc7336720e7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 9 Nov 2020 15:47:15 -0600 Subject: [PATCH 017/274] update network docs --- installation.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/installation.md b/installation.md index b0fa202824..e5cf33c9af 100644 --- a/installation.md +++ b/installation.md @@ -236,6 +236,17 @@ networks: - takeout ``` +In addition, you will need to add the `takeout` network to the root level `networks` entry in your `docker-compose.yml` file: + +```yaml +networks: + sail: + driver: bridge + takeout: + external: + name: takeout +``` + Once you have added your container to the `takeout` network, you may start Sail: ```bash From ceb3aa4449040bf931b7db4014933860a68edf78 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 09:46:48 -0600 Subject: [PATCH 018/274] clean up homestead references --- migrations.md | 2 -- mix.md | 2 +- valet.md | 10 ---------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/migrations.md b/migrations.md index a03ee063da..ba20df38ed 100644 --- a/migrations.md +++ b/migrations.md @@ -110,8 +110,6 @@ To run all of your outstanding migrations, execute the `migrate` Artisan command php artisan migrate -> {note} If you are using the [Homestead virtual machine](/docs/{{version}}/homestead), you should run this command from within your virtual machine. - #### Forcing Migrations To Run In Production diff --git a/mix.md b/mix.md index a066954820..0e87f415ae 100644 --- a/mix.md +++ b/mix.md @@ -43,7 +43,7 @@ Before triggering Mix, you must first ensure that Node.js and NPM are installed 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/). +You can easily install the latest version of Node and NPM using simple graphical installers downloaded from [the official Node website](https://nodejs.org/en/download/). #### Laravel Mix diff --git a/valet.md b/valet.md index 0e4258885c..4127bd7087 100644 --- a/valet.md +++ b/valet.md @@ -1,7 +1,6 @@ # Laravel Valet - [Introduction](#introduction) - - [Valet Or Homestead](#valet-or-homestead) - [Installation](#installation) - [Upgrading](#upgrading) - [Serving Sites](#serving-sites) @@ -61,15 +60,6 @@ Out of the box, Valet support includes, but is not limited to: However, you may extend Valet with your own [custom drivers](#custom-valet-drivers). - -### Valet Or Homestead - -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](https://brew.sh/) with commands like `brew install php` 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. - ## Installation From 4880fdcb330311149c3c2073cf6d65ae164a2758 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 10:37:33 -0600 Subject: [PATCH 019/274] remove jet flag reference --- authentication.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/authentication.md b/authentication.md index 512effa5fe..9f52498a5d 100644 --- a/authentication.md +++ b/authentication.md @@ -122,15 +122,6 @@ Laravel's `laravel/jetstream` package provides a quick way to scaffold all of th This command should be used on fresh applications and will install a layout view, registration and login views, as well as routes for all authentication end-points. A `/dashboard` route will also be generated to handle post-login requests to your application's dashboard. - -#### Creating Applications Including Authentication - -If you are starting a brand new application and would like to include the authentication scaffolding, you may use the `--jet` directive when creating your application via the Laravel Installer. This command will create a new application with all of the authentication scaffolding compiled and installed: - - laravel new kitetail --jet - -> {tip} To learn more about Jetstream, please visit the official [Jetstream documentation](https://jetstream.laravel.com). - ### Views From 4b897ef108aedd75fb6dea3d6dd02979d4bb5d5d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 10:50:04 -0600 Subject: [PATCH 020/274] clean up references to starter kits --- hashing.md | 2 +- passwords.md | 4 ++-- sanctum.md | 2 +- session.md | 2 +- verification.md | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hashing.md b/hashing.md index e9f33356bb..9151e4e41e 100644 --- a/hashing.md +++ b/hashing.md @@ -7,7 +7,7 @@ ## Introduction -The Laravel `Hash` [facade](/docs/{{version}}/facades) provides secure Bcrypt and Argon2 hashing for storing user passwords. If you are using the [Laravel Jetstream](https://jetstream.laravel.com) authentication scaffolding, Bcrypt will be used for registration and authentication by default. +The Laravel `Hash` [facade](/docs/{{version}}/facades) provides secure Bcrypt and Argon2 hashing for storing user passwords. If you are using the [Laravel authentication starter kits](/docs/{{version}}/starter-kits), Bcrypt will be used for registration and authentication by default. > {tip} Bcrypt is a great choice for hashing passwords because its "work factor" is adjustable, which means that the time it takes to generate a hash can be increased as hardware power increases. diff --git a/passwords.md b/passwords.md index 674335f8d9..eeb2e3bbd8 100644 --- a/passwords.md +++ b/passwords.md @@ -13,7 +13,7 @@ Most web applications provide a way for users to reset their forgotten passwords. Rather than forcing you to re-implement this on each application, Laravel provides convenient methods for sending password reminders and performing password resets. -> {tip} Want to get started fast? Install [Laravel Jetstream](https://jetstream.laravel.com) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. Jetstream will take care of scaffolding your entire authentication system, including resetting passwords! +> {tip} Want to get started fast? Install an authentication [starter kit](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system, including resetting passwords! ### Model Preparation @@ -72,7 +72,7 @@ Before moving on, let's examine this route in more detail. First, the request's The `sendResetLink` method returns a "status" slug. This status may be translated using Laravel's [localization](/docs/{{version}}/localization) helpers in order to display a user-friendly message to the user regarding the status of their request. The translation of the password reset status is determined by your application's `resources/lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. -> {tip} When manually implementing password resets, you are required to define the contents of the views and routes yourself. If you would like scaffolding that includes all necessary authentication and verification logic, check out [Laravel Jetstream](https://jetstream.laravel.com). +> {tip} When manually implementing password resets, you are required to define the contents of the views and routes yourself. If you would like scaffolding that includes all necessary authentication and verification logic, check out the [Laravel authentication starter kits](/docs/{{version}}/starter-kits). ### Resetting The Password diff --git a/sanctum.md b/sanctum.md index 1bf2acb758..e2191e8150 100644 --- a/sanctum.md +++ b/sanctum.md @@ -232,7 +232,7 @@ To authenticate your SPA, your SPA's login page should first make a request to t During this request Laravel will set an `XSRF-TOKEN` cookie containing the current CSRF token. This token should then be passed in an `X-XSRF-TOKEN` header on subsequent requests, which libraries like Axios and the Angular HttpClient will do automatically for you. -Once CSRF protection has been initialized, you should make a `POST` request to the typical Laravel `/login` route. This `/login` route may be provided by the `laravel/jetstream` [authentication scaffolding](/docs/{{version}}/authentication#introduction) package. +Once CSRF protection has been initialized, you should make a `POST` request to the typical Laravel `/login` route. This `/login` route may be implemented manually or using one of Laravel's [authentication starter kits](/docs/{{version}}/starter-kits). If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client. diff --git a/session.md b/session.md index cce9ae78d8..1b7ca46693 100644 --- a/session.md +++ b/session.md @@ -202,7 +202,7 @@ The `forget` method will remove a piece of data from the session. If you would l Regenerating the session ID is often done in order to prevent malicious users from exploiting a [session fixation](https://owasp.org/www-community/attacks/Session_fixation) attack on your application. -Laravel automatically regenerates the session ID during authentication if you are using [Laravel Jetstream](https://jetstream.laravel.com); however, if you need to manually regenerate the session ID, you may use the `regenerate` method. +Laravel automatically regenerates the session ID during authentication if you are using one of the Laravel [authentication starter kits](/docs/{{version}}/starter-kits); however, if you need to manually regenerate the session ID, you may use the `regenerate` method. $request->session()->regenerate(); diff --git a/verification.md b/verification.md index 1f91d2ff59..0903fcf952 100644 --- a/verification.md +++ b/verification.md @@ -15,7 +15,7 @@ Many web applications require users to verify their email addresses before using the application. Rather than forcing you to re-implement this on each application, Laravel provides convenient methods for sending and verifying email verification requests. -> {tip} Want to get started fast? Install [Laravel Jetstream](https://jetstream.laravel.com) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. Jetstream will take care of scaffolding your entire authentication system, including email verification support! +> {tip} Want to get started fast? Install one of the [Laravel authentication starter kits](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system, including email verification support! ### Model Preparation @@ -39,7 +39,7 @@ To get started, verify that your `App\Models\User` model implements the `Illumin Once this interface has been added to your model, newly registered users will automatically be sent an email containing an email verification link. As you can see by examining your `EventServiceProvider`, Laravel already contains a `SendEmailVerificationNotification` [listener](/docs/{{version}}/events) that is attached to the `Illuminate\Auth\Events\Registered` event. -If you are manually implementing registration within your application instead of using [Laravel Jetstream](https://jetstream.laravel.com), you should ensure that you are dispatching the `Illuminate\Auth\Events\Registered` event after a user's registration is successful: +If you are manually implementing registration within your application instead of using [a starter kit](/docs/{{version}}/starter-kits), you should ensure that you are dispatching the `Illuminate\Auth\Events\Registered` event after a user's registration is successful: use Illuminate\Auth\Events\Registered; @@ -68,7 +68,7 @@ As mentioned previously, a route should be defined that will return a view instr The route that returns the email verification notice should be named `verification.notice`. It is important that the route be assigned this exact name since the `verified` middleware [included with Laravel](#protecting-routes) will automatically redirect to this route name if a user has not verified their email address. -> {tip} When manually implementing email verification, you are required to define the contents of the verification notice view yourself. If you would like scaffolding that includes all necessary authentication and verification views, check out [Laravel Jetstream](https://jetstream.laravel.com). +> {tip} When manually implementing email verification, you are required to define the contents of the verification notice view yourself. If you would like scaffolding that includes all necessary authentication and verification views, check out the [Laravel authentication starter kits](/docs/{{version}}/starter-kits). ### The Email Verification Handler @@ -115,7 +115,7 @@ If an unverified user attempts to access a route that has been assigned this mid ## Events -When using [Laravel Jetstream](https://jetstream.laravel.com), Laravel dispatches [events](/docs/{{version}}/events) during the email verification process. If you are manually handling email verification for your application, you may wish to manually dispatch these events after verification is completed. You may attach listeners to these events in your `EventServiceProvider`: +When using the [Laravel authentication starter kits](/docs/{{version}}/starter-kits), Laravel dispatches [events](/docs/{{version}}/events) during the email verification process. If you are manually handling email verification for your application, you may wish to manually dispatch these events after verification is completed. You may attach listeners to these events in your `EventServiceProvider`: /** * The event listener mappings for the application. From 99989e44c2f9fd6d9bf4c7d2c114f9b5e8d4e82f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 10:59:56 -0600 Subject: [PATCH 021/274] authentication docs update --- authentication.md | 52 ++++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/authentication.md b/authentication.md index 9f52498a5d..e633dc0390 100644 --- a/authentication.md +++ b/authentication.md @@ -43,7 +43,7 @@ Don't worry if this all sounds confusing now! Many applications will never need #### Getting Started Fast -Want to get started fast? Install [Laravel Jetstream](https://jetstream.laravel.com) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. Jetstream will take care of scaffolding your entire authentication system! +Want to get started fast? Install a [Laravel authentication starter kit](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system! ### Database Considerations @@ -68,11 +68,15 @@ When a remote service needs to authenticate to access an API, cookies are not ty Laravel includes built-in authentication and session services which are typically accessed via the `Auth` and `Session` facades. These features provide cookie based authentication for requests that are initiated from web browsers. They provide methods that allow you to verify a user's credentials and authenticate the user. In addition, these services will automatically store the proper data in the user's session and issue the proper session cookie. A discussion of how to use these services is contained within this documentation. -**Jetstream / Fortify** +**Authentication Starter Kits** -As discussed in this documentation, you can interact with these authentication services manually to build your application's own authentication layer. However, to help you get started more quickly, we have released free packages that provide robust, modern scaffolding of the entire authentication layer. These packages are [Laravel Jetstream](https://jetstream.laravel.com) and [Laravel Fortify](https://github.com/laravel/fortify). +As discussed in this documentation, you can interact with these authentication services manually to build your application's own authentication layer. However, to help you get started more quickly, we have released [free packages](/docs/{{version}}/starter-kits) that provide robust, modern scaffolding of the entire authentication layer. These packages are [Laravel Breeze](https://github.com/laravel/breeze), [Laravel Jetstream](https://jetstream.laravel.com), and [Laravel Fortify](https://github.com/laravel/fortify). -Laravel Fortify is a headless authentication backend for Laravel that implements many of the features found in this documentation, including cookie-based authentication as well as other features such as two-factor authentication and email verification. Laravel Jetstream is a UI that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Laravel Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream, in addition to offering browser-based cookie authentication, includes built-in integration with Laravel Sanctum to offer API token authentication. Laravel's API authentication offerings are discussed below. +Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple Blade templates styled with [Tailwind](htts://tailwindcss.com). To get started, check out the documentation on Laravel's [authentication starter kits](/docs/{{version}}/starter-kits). + +Laravel Fortify is a headless authentication backend for Laravel that implements many of the features found in this documentation, including cookie-based authentication as well as other features such as two-factor authentication and email verification. + +Laravel Jetstream is a UI that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Laravel Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream, in addition to offering browser-based cookie authentication, includes two-factor authentication, team support, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below. #### Laravel's API Authentication Services @@ -89,7 +93,7 @@ In response to the complexity of OAuth2 and developer confusion, we set out to b Laravel Sanctum is a hybrid web / API authentication package that can manage your application's entire authentication process. This is possible because when Sanctum based applications receive a request, Sanctum will first determine if the request includes a session cookie that references an authenticated session. Sanctum accomplishes this by calling Laravel's built-in authentication services which we discussed earlier. If the request is not being authenticated via a session cookie, Sanctum will inspect the request for an API token. If an API token is present, Sanctum will authenticate the request using that token. To learn more about this process, please consult Sanctum's ["how it works"](/docs/{{version}}/sanctum#how-it-works) documentation. -Laravel Sanctum is the API package we have chosen to include with the [Laravel Jetstream](https://jetstream.laravel.com) authentication scaffolding because we believe it is the best fit for the majority of web application's authentication needs. +Laravel Sanctum is the API package we have chosen to include with the [Laravel Jetstream](https://jetstream.laravel.com) authentication starter kit because we believe it is the best fit for the majority of web application's authentication needs. #### Summary & Choosing Your Stack @@ -105,34 +109,12 @@ And, if you would like to get started quickly, we are pleased to recommend [Lara ## Authentication Quickstart -> {note} This portion of the documentation discusses authenticating users via the [Laravel Jetstream](https://jetstream.laravel.com) package, which includes UI scaffolding to help you get started quickly. If you would like to integrate with Laravel's authentication systems directly, check out the documentation on [manually authenticating users](#authenticating-users). - - -### Routing - -Laravel's `laravel/jetstream` package provides a quick way to scaffold all of the routes, views, and other backend logic needed for authentication using a few simple commands: - - composer require laravel/jetstream - - // Install Jetstream with the Livewire stack... - php artisan jetstream:install livewire - - // Install Jetstream with the Inertia stack... - php artisan jetstream:install inertia - -This command should be used on fresh applications and will install a layout view, registration and login views, as well as routes for all authentication end-points. A `/dashboard` route will also be generated to handle post-login requests to your application's dashboard. - - -### Views - -As mentioned in the previous section, the `laravel/jetstream` package's `php artisan jetstream:install` command will create all of the views you need for authentication and place them in the `resources/views/auth` directory. - -Jetstream will also create a `resources/views/layouts` directory containing a base layout for your application. All of these views use the [Tailwind CSS](https://tailwindcss.com) framework, but you are free to customize them however you wish. +> {note} This portion of the documentation discusses authenticating users via the [Laravel authentication starter kit](/docs/{{version}}/starter-kits), which includes UI scaffolding to help you get started quickly. If you would like to integrate with Laravel's authentication systems directly, check out the documentation on [manually authenticating users](#authenticating-users). - -### Authenticating + +### Install A Starter Kit -Now that your application has been scaffolded for authentication, you are ready to register and authenticate! You may simply access your application in a browser since Jetstream's authentication controllers already contain the logic to authenticate existing users and store new users in the database. +Something about installing a starter kit. #### Path Customization @@ -141,7 +123,7 @@ When a user is successfully authenticated, they will typically be redirected to public const HOME = '/home'; -When using Laravel Jetstream, the Jetstream installation process will change the value of the `HOME` constant to `/dashboard`. +> {tip} When using Laravel Breeze or Laravel Jetstream, the installation process will change the value of the `HOME` constant to `/dashboard`. ### Retrieving The Authenticated User @@ -228,14 +210,14 @@ When attaching the `auth` middleware to a route, you may also specify which guar ### Login Throttling -If you are using Laravel Jetstream, rate limiting will automatically be applied to login attempts. By default, the user will not be able to login for one minute if they fail to provide the correct credentials after several attempts. The throttling is unique to the user's username / e-mail address and their IP address. +If you are using Laravel Breeze or Laravel Jetstream, rate limiting will automatically be applied to login attempts. By default, the user will not be able to login for one minute if they fail to provide the correct credentials after several attempts. The throttling is unique to the user's username / e-mail address and their IP address. > {tip} If you would like to rate limit your own routes, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). ## Manually Authenticating Users -You are not required to use the authentication scaffolding included with Laravel Jetstream. If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch! +You are not required to use the authentication scaffolding included with an [authentication starter kit](/docs/{{version}}/starter-kits). If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch! We will access Laravel's authentication services via the `Auth` [facade](/docs/{{version}}/facades), so we'll need to make sure to import the `Auth` facade at the top of the class. Next, let's check out the `attempt` method: @@ -441,7 +423,7 @@ When the `logoutOtherDevices` method is invoked, the user's other sessions will While building your application, you may occasionally have actions that should require the user to confirm their password before the action is performed. Laravel includes built-in middleware to make this process a breeze. Implementing this feature will require you to define two routes: one route to display a view asking the user to confirm their password, and one route to confirm that the password is valid and redirect the user to their intended destination. -> {tip} The following documentation discusses how to integrate with Laravel's password confirmation features directly; however, if you would like to get started more quickly, the [Laravel Jetstream](https://jetstream.laravel.com) authentication scaffolding package includes support for this feature! +> {tip} The following documentation discusses how to integrate with Laravel's password confirmation features directly; however, if you would like to get started more quickly, the [Laravel authentication starter kits](/docs/{{version}}/starter-kits) include support for this feature! ### Configuration From 024e9bd1fb6e7f53bf1678869a2f1e0fa67ca180 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 11:09:18 -0600 Subject: [PATCH 022/274] docs on starter kits --- authentication.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/authentication.md b/authentication.md index e633dc0390..244cfbdfde 100644 --- a/authentication.md +++ b/authentication.md @@ -4,9 +4,7 @@ - [Database Considerations](#introduction-database-considerations) - [Ecosystem Overview](#ecosystem-overview) - [Authentication Quickstart](#authentication-quickstart) - - [Routing](#included-routing) - - [Views](#included-views) - - [Authenticating](#included-authenticating) + - [Install A Starter Kit](#install-a-starter-kit) - [Retrieving The Authenticated User](#retrieving-the-authenticated-user) - [Protecting Routes](#protecting-routes) - [Login Throttling](#login-throttling) @@ -72,11 +70,11 @@ Laravel includes built-in authentication and session services which are typicall As discussed in this documentation, you can interact with these authentication services manually to build your application's own authentication layer. However, to help you get started more quickly, we have released [free packages](/docs/{{version}}/starter-kits) that provide robust, modern scaffolding of the entire authentication layer. These packages are [Laravel Breeze](https://github.com/laravel/breeze), [Laravel Jetstream](https://jetstream.laravel.com), and [Laravel Fortify](https://github.com/laravel/fortify). -Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple Blade templates styled with [Tailwind](htts://tailwindcss.com). To get started, check out the documentation on Laravel's [authentication starter kits](/docs/{{version}}/starter-kits). +Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). To get started, check out the documentation on Laravel's [authentication starter kits](/docs/{{version}}/starter-kits). Laravel Fortify is a headless authentication backend for Laravel that implements many of the features found in this documentation, including cookie-based authentication as well as other features such as two-factor authentication and email verification. -Laravel Jetstream is a UI that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Laravel Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream, in addition to offering browser-based cookie authentication, includes two-factor authentication, team support, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below. +[Laravel Jetstream](https://jetstream.laravel.com) is a UI that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Laravel Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream, in addition to offering browser-based cookie authentication, includes two-factor authentication, team support, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below. #### Laravel's API Authentication Services @@ -109,12 +107,16 @@ And, if you would like to get started quickly, we are pleased to recommend [Lara ## Authentication Quickstart -> {note} This portion of the documentation discusses authenticating users via the [Laravel authentication starter kit](/docs/{{version}}/starter-kits), which includes UI scaffolding to help you get started quickly. If you would like to integrate with Laravel's authentication systems directly, check out the documentation on [manually authenticating users](#authenticating-users). +> {note} This portion of the documentation discusses authenticating users via the [Laravel authentication starter kits](/docs/{{version}}/starter-kits), which includes UI scaffolding to help you get started quickly. If you would like to integrate with Laravel's authentication systems directly, check out the documentation on [manually authenticating users](#authenticating-users). ### Install A Starter Kit -Something about installing a starter kit. +First, you should [install a Laravel authentication starter kit](/docs/{{version}}/starter-kits). Our current starter kits, Laravel Breeze and Laravel Jetstream, offer beautifully designed starting points for incorporating authentication into your fresh Laravel application. + +Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). + +[Laravel Jetstream](https://jetstream.laravel.com) is a more robust authentication starter kit that includes support for scaffolding your application with [Laravel Livewire](https://laravel-livewire.com) or [Inertia.js and Vue](https://inertiajs.com). In addition, Jetstream features optional support for two-factor authentication, teams, profile management, browser session management, API support via [Laravel Sanctum](/docs/{{version}}/sanctum), account deletion, and more. #### Path Customization @@ -123,7 +125,7 @@ When a user is successfully authenticated, they will typically be redirected to public const HOME = '/home'; -> {tip} When using Laravel Breeze or Laravel Jetstream, the installation process will change the value of the `HOME` constant to `/dashboard`. +> {tip} When using Laravel Breeze or Laravel Jetstream, the installation process will change the value of the `HOME` constant from its default value of `/home` to `/dashboard`. ### Retrieving The Authenticated User @@ -217,7 +219,7 @@ If you are using Laravel Breeze or Laravel Jetstream, rate limiting will automat ## Manually Authenticating Users -You are not required to use the authentication scaffolding included with an [authentication starter kit](/docs/{{version}}/starter-kits). If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch! +You are not required to use the authentication scaffolding included with the [authentication starter kits](/docs/{{version}}/starter-kits). If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch! We will access Laravel's authentication services via the `Auth` [facade](/docs/{{version}}/facades), so we'll need to make sure to import the `Auth` facade at the top of the class. Next, let's check out the `attempt` method: From b50c0973f3d1c66baa776a0ea9a511c400a38e9d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 11:10:48 -0600 Subject: [PATCH 023/274] start starter kit docs --- documentation.md | 1 + starter-kits.md | 1 + 2 files changed, 2 insertions(+) create mode 100644 starter-kits.md diff --git a/documentation.md b/documentation.md index 1e1ac4f4bc..39668ca627 100644 --- a/documentation.md +++ b/documentation.md @@ -7,6 +7,7 @@ - [Request Lifecycle](/docs/{{version}}/lifecycle) - [Configuration](/docs/{{version}}/configuration) - [Directory Structure](/docs/{{version}}/structure) + - [Starter Kits](/docs/{{version}}/starter-kits) - [Deployment](/docs/{{version}}/deployment) - ## Architecture Concepts - [Service Container](/docs/{{version}}/container) diff --git a/starter-kits.md b/starter-kits.md new file mode 100644 index 0000000000..e78f9f7095 --- /dev/null +++ b/starter-kits.md @@ -0,0 +1 @@ +# Starter Kits From a3692faef30f3d4bee50e8bd9ff82b6d6f024122 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 11:26:01 -0600 Subject: [PATCH 024/274] update references --- authentication.md | 20 ++++++++++---------- hashing.md | 2 +- passwords.md | 2 +- sanctum.md | 2 +- session.md | 2 +- verification.md | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/authentication.md b/authentication.md index 244cfbdfde..ac1c7892b8 100644 --- a/authentication.md +++ b/authentication.md @@ -41,7 +41,7 @@ Don't worry if this all sounds confusing now! Many applications will never need #### Getting Started Fast -Want to get started fast? Install a [Laravel authentication starter kit](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system! +Want to get started fast? Install a [Laravel application starter kit](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system! ### Database Considerations @@ -66,15 +66,15 @@ When a remote service needs to authenticate to access an API, cookies are not ty Laravel includes built-in authentication and session services which are typically accessed via the `Auth` and `Session` facades. These features provide cookie based authentication for requests that are initiated from web browsers. They provide methods that allow you to verify a user's credentials and authenticate the user. In addition, these services will automatically store the proper data in the user's session and issue the proper session cookie. A discussion of how to use these services is contained within this documentation. -**Authentication Starter Kits** +**Application Starter Kits** As discussed in this documentation, you can interact with these authentication services manually to build your application's own authentication layer. However, to help you get started more quickly, we have released [free packages](/docs/{{version}}/starter-kits) that provide robust, modern scaffolding of the entire authentication layer. These packages are [Laravel Breeze](https://github.com/laravel/breeze), [Laravel Jetstream](https://jetstream.laravel.com), and [Laravel Fortify](https://github.com/laravel/fortify). -Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). To get started, check out the documentation on Laravel's [authentication starter kits](/docs/{{version}}/starter-kits). +Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). To get started, check out the documentation on Laravel's [application starter kits](/docs/{{version}}/starter-kits). Laravel Fortify is a headless authentication backend for Laravel that implements many of the features found in this documentation, including cookie-based authentication as well as other features such as two-factor authentication and email verification. -[Laravel Jetstream](https://jetstream.laravel.com) is a UI that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Laravel Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream, in addition to offering browser-based cookie authentication, includes two-factor authentication, team support, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below. +[Laravel Jetstream](https://jetstream.laravel.com) is an application starter kit that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Laravel Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream includes optional support for two-factor authentication, team support, browser session management, profile management, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below. #### Laravel's API Authentication Services @@ -91,7 +91,7 @@ In response to the complexity of OAuth2 and developer confusion, we set out to b Laravel Sanctum is a hybrid web / API authentication package that can manage your application's entire authentication process. This is possible because when Sanctum based applications receive a request, Sanctum will first determine if the request includes a session cookie that references an authenticated session. Sanctum accomplishes this by calling Laravel's built-in authentication services which we discussed earlier. If the request is not being authenticated via a session cookie, Sanctum will inspect the request for an API token. If an API token is present, Sanctum will authenticate the request using that token. To learn more about this process, please consult Sanctum's ["how it works"](/docs/{{version}}/sanctum#how-it-works) documentation. -Laravel Sanctum is the API package we have chosen to include with the [Laravel Jetstream](https://jetstream.laravel.com) authentication starter kit because we believe it is the best fit for the majority of web application's authentication needs. +Laravel Sanctum is the API package we have chosen to include with the [Laravel Jetstream](https://jetstream.laravel.com) application starter kit because we believe it is the best fit for the majority of web application's authentication needs. #### Summary & Choosing Your Stack @@ -107,16 +107,16 @@ And, if you would like to get started quickly, we are pleased to recommend [Lara ## Authentication Quickstart -> {note} This portion of the documentation discusses authenticating users via the [Laravel authentication starter kits](/docs/{{version}}/starter-kits), which includes UI scaffolding to help you get started quickly. If you would like to integrate with Laravel's authentication systems directly, check out the documentation on [manually authenticating users](#authenticating-users). +> {note} This portion of the documentation discusses authenticating users via the [Laravel application starter kits](/docs/{{version}}/starter-kits), which includes UI scaffolding to help you get started quickly. If you would like to integrate with Laravel's authentication systems directly, check out the documentation on [manually authenticating users](#authenticating-users). ### Install A Starter Kit -First, you should [install a Laravel authentication starter kit](/docs/{{version}}/starter-kits). Our current starter kits, Laravel Breeze and Laravel Jetstream, offer beautifully designed starting points for incorporating authentication into your fresh Laravel application. +First, you should [install a Laravel application starter kit](/docs/{{version}}/starter-kits). Our current starter kits, Laravel Breeze and Laravel Jetstream, offer beautifully designed starting points for incorporating authentication into your fresh Laravel application. Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). -[Laravel Jetstream](https://jetstream.laravel.com) is a more robust authentication starter kit that includes support for scaffolding your application with [Laravel Livewire](https://laravel-livewire.com) or [Inertia.js and Vue](https://inertiajs.com). In addition, Jetstream features optional support for two-factor authentication, teams, profile management, browser session management, API support via [Laravel Sanctum](/docs/{{version}}/sanctum), account deletion, and more. +[Laravel Jetstream](https://jetstream.laravel.com) is a more robust application starter kit that includes support for scaffolding your application with [Laravel Livewire](https://laravel-livewire.com) or [Inertia.js and Vue](https://inertiajs.com). In addition, Jetstream features optional support for two-factor authentication, teams, profile management, browser session management, API support via [Laravel Sanctum](/docs/{{version}}/sanctum), account deletion, and more. #### Path Customization @@ -219,7 +219,7 @@ If you are using Laravel Breeze or Laravel Jetstream, rate limiting will automat ## Manually Authenticating Users -You are not required to use the authentication scaffolding included with the [authentication starter kits](/docs/{{version}}/starter-kits). If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch! +You are not required to use the authentication scaffolding included with the [application starter kits](/docs/{{version}}/starter-kits). If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch! We will access Laravel's authentication services via the `Auth` [facade](/docs/{{version}}/facades), so we'll need to make sure to import the `Auth` facade at the top of the class. Next, let's check out the `attempt` method: @@ -425,7 +425,7 @@ When the `logoutOtherDevices` method is invoked, the user's other sessions will While building your application, you may occasionally have actions that should require the user to confirm their password before the action is performed. Laravel includes built-in middleware to make this process a breeze. Implementing this feature will require you to define two routes: one route to display a view asking the user to confirm their password, and one route to confirm that the password is valid and redirect the user to their intended destination. -> {tip} The following documentation discusses how to integrate with Laravel's password confirmation features directly; however, if you would like to get started more quickly, the [Laravel authentication starter kits](/docs/{{version}}/starter-kits) include support for this feature! +> {tip} The following documentation discusses how to integrate with Laravel's password confirmation features directly; however, if you would like to get started more quickly, the [Laravel application starter kits](/docs/{{version}}/starter-kits) include support for this feature! ### Configuration diff --git a/hashing.md b/hashing.md index 9151e4e41e..9a7fc4cb14 100644 --- a/hashing.md +++ b/hashing.md @@ -7,7 +7,7 @@ ## Introduction -The Laravel `Hash` [facade](/docs/{{version}}/facades) provides secure Bcrypt and Argon2 hashing for storing user passwords. If you are using the [Laravel authentication starter kits](/docs/{{version}}/starter-kits), Bcrypt will be used for registration and authentication by default. +The Laravel `Hash` [facade](/docs/{{version}}/facades) provides secure Bcrypt and Argon2 hashing for storing user passwords. If you are using the [Laravel application starter kits](/docs/{{version}}/starter-kits), Bcrypt will be used for registration and authentication by default. > {tip} Bcrypt is a great choice for hashing passwords because its "work factor" is adjustable, which means that the time it takes to generate a hash can be increased as hardware power increases. diff --git a/passwords.md b/passwords.md index eeb2e3bbd8..4a5b898cc2 100644 --- a/passwords.md +++ b/passwords.md @@ -72,7 +72,7 @@ Before moving on, let's examine this route in more detail. First, the request's The `sendResetLink` method returns a "status" slug. This status may be translated using Laravel's [localization](/docs/{{version}}/localization) helpers in order to display a user-friendly message to the user regarding the status of their request. The translation of the password reset status is determined by your application's `resources/lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. -> {tip} When manually implementing password resets, you are required to define the contents of the views and routes yourself. If you would like scaffolding that includes all necessary authentication and verification logic, check out the [Laravel authentication starter kits](/docs/{{version}}/starter-kits). +> {tip} When manually implementing password resets, you are required to define the contents of the views and routes yourself. If you would like scaffolding that includes all necessary authentication and verification logic, check out the [Laravel application starter kits](/docs/{{version}}/starter-kits). ### Resetting The Password diff --git a/sanctum.md b/sanctum.md index e2191e8150..a90254f470 100644 --- a/sanctum.md +++ b/sanctum.md @@ -232,7 +232,7 @@ To authenticate your SPA, your SPA's login page should first make a request to t During this request Laravel will set an `XSRF-TOKEN` cookie containing the current CSRF token. This token should then be passed in an `X-XSRF-TOKEN` header on subsequent requests, which libraries like Axios and the Angular HttpClient will do automatically for you. -Once CSRF protection has been initialized, you should make a `POST` request to the typical Laravel `/login` route. This `/login` route may be implemented manually or using one of Laravel's [authentication starter kits](/docs/{{version}}/starter-kits). +Once CSRF protection has been initialized, you should make a `POST` request to the typical Laravel `/login` route. This `/login` route may be implemented manually or using one of Laravel's [application starter kits](/docs/{{version}}/starter-kits). If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client. diff --git a/session.md b/session.md index 1b7ca46693..ed038d8285 100644 --- a/session.md +++ b/session.md @@ -202,7 +202,7 @@ The `forget` method will remove a piece of data from the session. If you would l Regenerating the session ID is often done in order to prevent malicious users from exploiting a [session fixation](https://owasp.org/www-community/attacks/Session_fixation) attack on your application. -Laravel automatically regenerates the session ID during authentication if you are using one of the Laravel [authentication starter kits](/docs/{{version}}/starter-kits); however, if you need to manually regenerate the session ID, you may use the `regenerate` method. +Laravel automatically regenerates the session ID during authentication if you are using one of the Laravel [application starter kits](/docs/{{version}}/starter-kits); however, if you need to manually regenerate the session ID, you may use the `regenerate` method. $request->session()->regenerate(); diff --git a/verification.md b/verification.md index 0903fcf952..93c611ae2e 100644 --- a/verification.md +++ b/verification.md @@ -15,7 +15,7 @@ Many web applications require users to verify their email addresses before using the application. Rather than forcing you to re-implement this on each application, Laravel provides convenient methods for sending and verifying email verification requests. -> {tip} Want to get started fast? Install one of the [Laravel authentication starter kits](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system, including email verification support! +> {tip} Want to get started fast? Install one of the [Laravel application starter kits](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system, including email verification support! ### Model Preparation @@ -68,7 +68,7 @@ As mentioned previously, a route should be defined that will return a view instr The route that returns the email verification notice should be named `verification.notice`. It is important that the route be assigned this exact name since the `verified` middleware [included with Laravel](#protecting-routes) will automatically redirect to this route name if a user has not verified their email address. -> {tip} When manually implementing email verification, you are required to define the contents of the verification notice view yourself. If you would like scaffolding that includes all necessary authentication and verification views, check out the [Laravel authentication starter kits](/docs/{{version}}/starter-kits). +> {tip} When manually implementing email verification, you are required to define the contents of the verification notice view yourself. If you would like scaffolding that includes all necessary authentication and verification views, check out the [Laravel application starter kits](/docs/{{version}}/starter-kits). ### The Email Verification Handler @@ -115,7 +115,7 @@ If an unverified user attempts to access a route that has been assigned this mid ## Events -When using the [Laravel authentication starter kits](/docs/{{version}}/starter-kits), Laravel dispatches [events](/docs/{{version}}/events) during the email verification process. If you are manually handling email verification for your application, you may wish to manually dispatch these events after verification is completed. You may attach listeners to these events in your `EventServiceProvider`: +When using the [Laravel application starter kits](/docs/{{version}}/starter-kits), Laravel dispatches [events](/docs/{{version}}/events) during the email verification process. If you are manually handling email verification for your application, you may wish to manually dispatch these events after verification is completed. You may attach listeners to these events in your `EventServiceProvider`: /** * The event listener mappings for the application. From 70b5cdc5e46f234aef406a34cf417c6e6f991146 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 13:29:22 -0600 Subject: [PATCH 025/274] add note on command usage --- installation.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/installation.md b/installation.md index e5cf33c9af..6b731f2072 100644 --- a/installation.md +++ b/installation.md @@ -203,6 +203,16 @@ When using Laravel Sail, your application is executing within a Docker container ./sail npm run prod ``` +When reading the Laravel documentation, you will often see references to Artisan and Composer commands that do not reference Sail. Those examples assume that PHP is installed on your local computer. If you are using Sail for your local Laravel development environment, you should execute those commands using Sail: + +```bash +// Running Artisan commands locally... +php artisan queue:work + +// Running Artisan commands within Laravel Sail... +./sail artisan queue:work +``` + ### Interacting With Databases @@ -283,3 +293,5 @@ After running this command, the Dockerfiles and other configuration files used b ## Next Steps Now that you have created your Laravel project, you may wondering what to learn next. We recommend becoming familiar with how Laravel works by reading our documentation on Laravel's [request lifecycle](/docs/{{version}}/lifecycle), [configuration](/docs/{{version}}/configuration), or [directory structure](/docs/{{version}}/structure). Or, if you want to jump straight into coding, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). + +> {tip} If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits). From 1ed35cf2e22f0157b7c28b7c116febb19e558157 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 13:29:53 -0600 Subject: [PATCH 026/274] add forge heading --- deployment.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deployment.md b/deployment.md index 3e5f549982..d8302f63f4 100644 --- a/deployment.md +++ b/deployment.md @@ -129,6 +129,9 @@ The debug option in your config/app.php configuration file determines how much i ## Deploying With Forge / Vapor + +#### Laravel Forge + If you aren't quite ready to manage your own server configuration or aren't comfortable configuring all of the various services needed to run a robust Laravel application, [Laravel Forge](https://forge.laravel.com) is a wonderful alternative. Laravel Forge can create servers on various infrastructure providers such as DigitalOcean, Linode, AWS, and more. In addition, Forge installs and manages all of the tools needed to build robust Laravel applications, such as Nginx, MySQL, Redis, Memcached, Beanstalk, and more. From f1b256d46400f9dcd0b559d79be2b3e2b70d0dbf Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 13:44:28 -0600 Subject: [PATCH 027/274] starter kit dcos --- starter-kits.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/starter-kits.md b/starter-kits.md index e78f9f7095..106b7d3148 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -1 +1,42 @@ # Starter Kits + +- [Laravel Breeze](#laravel-breeze) + - [Installation](#laravel-breeze-installation) +- [Laravel Jetstream](#laravel-jetstream) + + +## Laravel Breeze + +Laravel Breeze is a minimal, simple implementation of all of Laravel's [authentication features](/docs/{{version}}/authentication), including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). + + +### Installation + +First, you should [create a new Laravel application](/docs/{{version}}/installation), configure your database, and run your [database migrations](/docs/{{version}}/migrations): + +```bash +curl -s https://laravel.build/my-app | bash + +cd my-app + +php artisan migrate +``` + +Once you have created a new Laravel application, you may install Laravel Breeze using Composer: + + composer require laravel/breeze --dev + +After Composer has installed the Laravel Breeze package, you may run the `breeze:install` Artisan command. This command publishes the authentication views, routes, controllers, and other resources to your application. Laravel Breeze publishes all of its code to your application so that you have full control and visibility over its features and implementation. After Breeze is installed, you should also compile your assets so that your application's CSS file is available: + + php artisan breeze:install + + npm install && npm run dev + +Next, you may navigate to your application's `/login` or `/register` URLs in your web browser. All of Breeze's routes are defined within the `routes/auth.php` file. + + +## Laravel Jetstream + +Laravel Jetstream is a beautifully designed application scaffolding for Laravel. Jetstream provides the perfect starting point for your next Laravel application and includes login, registration, email verification, two-factor authentication, session management, API support via Laravel Sanctum, and optional team management. Jetstream is designed using [Tailwind CSS](https://tailwindcss.com) and offers your choice of [Livewire](https://laravel-livewire.com) or [Inertia.js](https://inertiajs.com) scaffolding. + +Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/1.x/introduction.html). From 0e74974010ec8b9e22da3f6ef7db8e34628279bd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 10 Nov 2020 13:50:12 -0600 Subject: [PATCH 028/274] update references --- authentication.md | 4 ++-- starter-kits.md | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/authentication.md b/authentication.md index ac1c7892b8..2039e99ea7 100644 --- a/authentication.md +++ b/authentication.md @@ -74,7 +74,7 @@ Laravel Breeze is a minimal, simple implementation of all of Laravel's authentic Laravel Fortify is a headless authentication backend for Laravel that implements many of the features found in this documentation, including cookie-based authentication as well as other features such as two-factor authentication and email verification. -[Laravel Jetstream](https://jetstream.laravel.com) is an application starter kit that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Laravel Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream includes optional support for two-factor authentication, team support, browser session management, profile management, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below. +[Laravel Jetstream](https://jetstream.laravel.com) is an application starter kit that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream includes optional support for two-factor authentication, team support, browser session management, profile management, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below. #### Laravel's API Authentication Services @@ -116,7 +116,7 @@ First, you should [install a Laravel application starter kit](/docs/{{version}}/ Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). -[Laravel Jetstream](https://jetstream.laravel.com) is a more robust application starter kit that includes support for scaffolding your application with [Laravel Livewire](https://laravel-livewire.com) or [Inertia.js and Vue](https://inertiajs.com). In addition, Jetstream features optional support for two-factor authentication, teams, profile management, browser session management, API support via [Laravel Sanctum](/docs/{{version}}/sanctum), account deletion, and more. +[Laravel Jetstream](https://jetstream.laravel.com) is a more robust application starter kit that includes support for scaffolding your application with [Livewire](https://laravel-livewire.com) or [Inertia.js and Vue](https://inertiajs.com). In addition, Jetstream features optional support for two-factor authentication, teams, profile management, browser session management, API support via [Laravel Sanctum](/docs/{{version}}/sanctum), account deletion, and more. #### Path Customization diff --git a/starter-kits.md b/starter-kits.md index 106b7d3148..5a88ea26c9 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -7,7 +7,7 @@ ## Laravel Breeze -Laravel Breeze is a minimal, simple implementation of all of Laravel's [authentication features](/docs/{{version}}/authentication), including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). +Laravel Breeze is a minimal, simple implementation of all of Laravel's [authentication features](/docs/{{version}}/authentication), including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). Breeze provides a wonderful starting point for beginning a fresh Laravel application. ### Installation @@ -37,6 +37,8 @@ Next, you may navigate to your application's `/login` or `/register` URLs in you ## Laravel Jetstream -Laravel Jetstream is a beautifully designed application scaffolding for Laravel. Jetstream provides the perfect starting point for your next Laravel application and includes login, registration, email verification, two-factor authentication, session management, API support via Laravel Sanctum, and optional team management. Jetstream is designed using [Tailwind CSS](https://tailwindcss.com) and offers your choice of [Livewire](https://laravel-livewire.com) or [Inertia.js](https://inertiajs.com) scaffolding. +While Laravel Breeze provides a simple and minimal starting point for building a Laravel application, Jetstream augments that functionality with more robust features and additional frontend technology stacks. **For those brand new to Laravel, we recommend learning the ropes with Laravel Breeze before graduating to Laravel Jetstream.** + +Jetstream provides a beautifully designed application scaffolding for Laravel and includes login, registration, email verification, two-factor authentication, session management, API support via Laravel Sanctum, and optional team management. Jetstream is designed using [Tailwind CSS](https://tailwindcss.com) and offers your choice of [Livewire](https://laravel-livewire.com) or [Inertia.js](https://inertiajs.com) driven frontend scaffolding. Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/1.x/introduction.html). From 257602f28cad41d326c596b97443bc6458826359 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 10:07:34 -0600 Subject: [PATCH 029/274] change wording on sanctum --- sanctum.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanctum.md b/sanctum.md index 3df932b919..a8f96bd9a6 100644 --- a/sanctum.md +++ b/sanctum.md @@ -232,7 +232,7 @@ To authenticate your SPA, your SPA's login page should first make a request to t During this request Laravel will set an `XSRF-TOKEN` cookie containing the current CSRF token. This token should then be passed in an `X-XSRF-TOKEN` header on subsequent requests, which libraries like Axios and the Angular HttpClient will do automatically for you. -Once CSRF protection has been initialized, you should make a `POST` request to the typical Laravel `/login` route. This `/login` route may be implemented manually or using one of Laravel's [application starter kits](/docs/{{version}}/starter-kits). +Once CSRF protection has been initialized, you should make a `POST` request to the your Laravel application's `/login` route. This `/login` route may be implemented manually or using one of Laravel's [application starter kits](/docs/{{version}}/starter-kits). If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client. From 3719815837c0292fd2128c43a3ec6a999b84ce53 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 10:54:35 -0600 Subject: [PATCH 030/274] working on next steps --- installation.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 6b731f2072..6f82de26ab 100644 --- a/installation.md +++ b/installation.md @@ -17,6 +17,8 @@ - [Container CLI](#sail-container-cli) - [Customization](#sail-customization) - [Next Steps](#next-steps) + - [Laravel The Full Stack Framework](#laravel-the-fullstack-framework) + - [Laravel The API Backend](#laravel-the-api-backend) ## Meet Laravel @@ -292,6 +294,23 @@ After running this command, the Dockerfiles and other configuration files used b ## Next Steps -Now that you have created your Laravel project, you may wondering what to learn next. We recommend becoming familiar with how Laravel works by reading our documentation on Laravel's [request lifecycle](/docs/{{version}}/lifecycle), [configuration](/docs/{{version}}/configuration), or [directory structure](/docs/{{version}}/structure). Or, if you want to jump straight into coding, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). +Now that you have created your Laravel project, you may be wondering what to learn next. First, we strongly recommend becoming familiar with how Laravel works by reading our documentation on Laravel's [request lifecycle](/docs/{{version}}/lifecycle), [configuration](/docs/{{version}}/configuration), or [directory structure](/docs/{{version}}/structure). In addition, you should read the documentation on Laravel's [service container](/docs/{{version}}/container) and [facades](/docs/{{version}}/facades). + +How you want to use Laravel will also dictate the next steps on your journey. There are a variety of ways to use Laravel, and we'll explore two primary use cases for the framework below. + + +### Laravel The Full Stack Framework + +Laravel may serve as a full stack framework. By "full stack" framework we mean that you are going to use Laravel to route requests to your application and render your frontend via [Blade templates](/docs/{{version}}/blade) or using an single-page application hybrid technology like [Inertia.js](https://inertiajs.com). This is the most common way to use the Laravel framework. + +If this is how you plan to use Laravel, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). In addition, you might be interested in learning about community packages like [Livewire](https://laravel-livewire.com) and [Inertia.js](https://inertiajs.com). These packages allow you to use Laravel as a full-stack framework while enjoying many of the UI benefits provided by single-page JavaScript applications. > {tip} If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits). + + +### Laravel The API Backend + +Laravel my also serve as an API backend to a JavaScript single-page application or mobile application. For example, you might use Laravel as an API backend for your [Next.js](https://nextjs.org) application. In this context, you may use Laravel to provide [authentication](/docs/{{version}}/sanctum) and data storage / retrieval for your application, while also taking advantage of Laravel's powerful services such as queues, emails, notifications, and more. + +If this is how you plan to use Laravel, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [Laravel Sanctum](/docs/{{version}}/sanctum), and the [Eloquent ORM](/docs/{{version}}/eloquent). + From 7059986c39b0e2c6382dc554c65c03f708f8cb92 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 11:09:54 -0600 Subject: [PATCH 031/274] wip --- homestead.md | 2 +- installation.md | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/homestead.md b/homestead.md index a4e3b706d5..aa292cfbdf 100644 --- a/homestead.md +++ b/homestead.md @@ -249,7 +249,7 @@ The specified services will be started or stopped based on their order in the `e #### Hostname Resolution -Homestead publishes hostnames over `mDNS` for automatic host resolution. If you set `hostname: homestead` in your `Homestead.yaml` file, the host will be available at `homestead.local`. MacOS, iOS, and Linux desktop distributions include `mDNS` support by default. Windows requires installing [Bonjour Print Services for Windows](https://support.apple.com/kb/DL999?viewlocale=en_US&locale=en_US). +Homestead publishes hostnames over `mDNS` for automatic host resolution. If you set `hostname: homestead` in your `Homestead.yaml` file, the host will be available at `homestead.local`. macOS, iOS, and Linux desktop distributions include `mDNS` support by default. Windows requires installing [Bonjour Print Services for Windows](https://support.apple.com/kb/DL999?viewlocale=en_US&locale=en_US). Using automatic hostnames works best for "per project" installations of Homestead. If you host multiple sites on a single Homestead instance, you may add the "domains" for your web sites to the `hosts` file on your machine. The `hosts` file will redirect requests for your Homestead sites into your Homestead machine. On Mac and Linux, this file is located at `/etc/hosts`. On Windows, it is located at `C:\Windows\System32\drivers\etc\hosts`. The lines you add to this file will look like the following: diff --git a/installation.md b/installation.md index 6f82de26ab..66a691f141 100644 --- a/installation.md +++ b/installation.md @@ -3,7 +3,7 @@ - [Meet Laravel](#meet-laravel) - [Why Laravel?](#why-laravel) - [Your First Laravel Project](#your-first-laravel-project) - - [Getting Started On MacOS](#getting-started-on-macos) + - [Getting Started On macOS](#getting-started-on-macos) - [Getting Started On Windows](#getting-started-on-windows) - [Getting Started On Linux](#getting-started-on-linux) - [Installation Via Composer](#installation-via-composer) @@ -62,7 +62,7 @@ Laravel Sail is a light-weight command-line interface for interacting with Larav > {tip} Already a Docker expert? Don't worry! Everything about Sail can be customized using the `docker-compose.yml` file included with Laravel. -### Getting Started On MacOS +### Getting Started On macOS If you're developing on a Mac and [Docker Desktop](https://www.docker.com/products/docker-desktop) is already installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: @@ -145,6 +145,25 @@ If your computer already has PHP and Composer installed, you may create a new La php artisan serve + +#### The Laravel Installer + +Or, you may install the Laravel Installer as a global Composer dependency: + + composer global require laravel/installer + + laravel new my-app + + php artisan serve + +Make sure to place Composer's system-wide vendor bin directory in your `$PATH` so the `laravel` executable can be located by your system. This directory exists in different locations based on your operating system; however, some common locations include: + +
+- macOS: `$HOME/.composer/vendor/bin` +- Windows: `%USERPROFILE%\AppData\Roaming\Composer\vendor\bin` +- GNU / Linux Distributions: `$HOME/.config/composer/vendor/bin` or `$HOME/.composer/vendor/bin` +
+ ## Initial Configuration From ae41efedcb68c5c56b807aff2593cfae5c85f83b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 14:39:21 -0600 Subject: [PATCH 032/274] update container docs --- container.md | 61 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/container.md b/container.md index d4ea262fa1..30f0eb849a 100644 --- a/container.md +++ b/container.md @@ -74,13 +74,11 @@ A deep understanding of the Laravel service container is essential to building a ### Binding Basics -Almost all of your service container bindings will be registered within [service providers](/docs/{{version}}/providers), so most of these examples will demonstrate using the container in that context. - -> {tip} There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection. - #### Simple Bindings +Almost all of your service container bindings will be registered within [service providers](/docs/{{version}}/providers), so most of these examples will demonstrate using the container in that context. + Within a service provider, you always have access to the container via the `$this->app` property. We can register a binding using the `bind` method, passing the class or interface name that we wish to register along with a `Closure` that returns an instance of the class: $this->app->bind('HelpSpot\API', function ($app) { @@ -89,6 +87,16 @@ Within a service provider, you always have access to the container via the `$thi Note that we receive the container itself as an argument to the resolver. We can then use the container to resolve sub-dependencies of the object we are building. +As mentioned, you will typically be interacting with the container within service providers; however, if you would like to interact with the container outside of a service provider, you may do so via the `App` [facade](/docs/{{version}}/facades): + + use Illuminate\Support\Facades\App; + + App::bind('HelpSpot\API', function ($app) { + // ... + }); + +> {tip} There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection. + #### Binding A Singleton @@ -117,7 +125,7 @@ A very powerful feature of the service container is its ability to bind an inter 'App\Services\RedisEventPusher' ); -This statement tells the container that it should inject the `RedisEventPusher` when a class needs an implementation of `EventPusher`. Now we can type-hint the `EventPusher` interface in a constructor, or any other location where dependencies are injected by the service container: +This statement tells the container that it should inject the `RedisEventPusher` when a class needs an implementation of `EventPusher`. Now we can type-hint the `EventPusher` interface in the constructor of a class that is resolved by the container. Remember, controllers, event listeners, middleware, and various other types of classes within Laravel applications are always resolved using the container: use App\Contracts\EventPusher; @@ -252,24 +260,43 @@ The `extend` method allows the modification of resolved services. For example, w ## Resolving -#### The `make` Method +### The `make` Method -You may use the `make` method to resolve a class instance out of the container. The `make` method accepts the name of the class or interface you wish to resolve: +You may use the `make` method to resolve a class instance from the container. The `make` method accepts the name of the class or interface you wish to resolve: $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 `resolve` helper: +If you are outside of a service provider in a location of your code that does not have access to the `$app` variable, you may use the `App` [facade](/docs/{{version}}/facades) to resolve a class instance from the container: + + use Illuminate\Support\Facades\App; + + $api = App::make('HelpSpot\API'); - $api = resolve('HelpSpot\API'); +If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the `makeWith` method. For example, we may manually pass the `$id` constructor argument required by the `HelpSpot\API` service: -If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the `makeWith` method: + use Illuminate\Support\Facades\App; - $api = $this->app->makeWith('HelpSpot\API', ['id' => 1]); + $api = App::makeWith('HelpSpot\API', ['id' => 1]); + +If you would like to have the Laravel container instance itself injected into a class that is being resolved by the container, you may type-hint the `Illuminate\Container\Container` class on your class' constructor: + + use Illuminate\Container\Container; + + /** + * Create a new class instance. + * + * @param \Illuminate\Container\Container + * @return void + */ + public function __construct(Container $container) + { + $this->container = $container; + } -#### Automatic Injection +### Automatic Injection -Alternatively, and importantly, you may "type-hint" the dependency in the constructor of a class that is resolved by the container, including [controllers](/docs/{{version}}/controllers), [event listeners](/docs/{{version}}/events), [middleware](/docs/{{version}}/middleware), and more. Additionally, you may type-hint dependencies in the `handle` method of [queued jobs](/docs/{{version}}/queues). In practice, this is how most of your objects should be resolved by the container. +Alternatively, and importantly, you may type-hint the dependency in the constructor of a class that is resolved by the container, including [controllers](/docs/{{version}}/controllers), [event listeners](/docs/{{version}}/events), [middleware](/docs/{{version}}/middleware), and more. Additionally, you may type-hint dependencies in the `handle` method of [queued jobs](/docs/{{version}}/queues). In practice, this is how most of your objects should be resolved by the container. For example, you may type-hint a repository defined by your application in a controller's constructor. The repository will automatically be resolved and injected into the class: @@ -277,19 +304,21 @@ For example, you may type-hint a repository defined by your application in a con namespace App\Http\Controllers; - use App\Models\Users\Repository as UserRepository; + use App\Repositories\UserRepository; class UserController extends Controller { /** * The user repository instance. + * + * @var \App\Repositories\UserRepository */ protected $users; /** * Create a new controller instance. * - * @param UserRepository $users + * @param \App\Repositories\UserRepository $users * @return void */ public function __construct(UserRepository $users) @@ -301,7 +330,7 @@ For example, you may type-hint a repository defined by your application in a con * Show the user with the given ID. * * @param int $id - * @return Response + * @return \Illuminate\Http\Response */ public function show($id) { From 85c1f4af944e4ac03ab32c32697a45f64f2161d2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 14:52:09 -0600 Subject: [PATCH 033/274] update container examples --- container.md | 94 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 27 deletions(-) diff --git a/container.md b/container.md index 30f0eb849a..1d89c80c94 100644 --- a/container.md +++ b/container.md @@ -81,17 +81,21 @@ Almost all of your service container bindings will be registered within [service Within a service provider, you always have access to the container via the `$this->app` property. We can register a binding using the `bind` method, passing the class or interface name that we wish to register along with a `Closure` that returns an instance of the class: - $this->app->bind('HelpSpot\API', function ($app) { - return new \HelpSpot\API($app->make('HttpClient')); + use App\Services\Transistor; + use App\Services\PodcastParser; + + $this->app->bind(Transistor::class, function ($app) { + return new Transistor($app->make(PodcastParser::class)); }); Note that we receive the container itself as an argument to the resolver. We can then use the container to resolve sub-dependencies of the object we are building. As mentioned, you will typically be interacting with the container within service providers; however, if you would like to interact with the container outside of a service provider, you may do so via the `App` [facade](/docs/{{version}}/facades): + use App\Services\Transistor; use Illuminate\Support\Facades\App; - App::bind('HelpSpot\API', function ($app) { + App::bind(Transistor::class, function ($app) { // ... }); @@ -102,8 +106,11 @@ As mentioned, you will typically be interacting with the container within servic The `singleton` method binds a class or interface into the container that should only be resolved one time. Once a singleton binding is resolved, the same object instance will be returned on subsequent calls into the container: - $this->app->singleton('HelpSpot\API', function ($app) { - return new \HelpSpot\API($app->make('HttpClient')); + use App\Services\Transistor; + use App\Services\PodcastParser; + + $this->app->singleton(Transistor::class, function ($app) { + return new Transistor($app->make(PodcastParser::class)); }); @@ -111,19 +118,22 @@ The `singleton` method binds a class or interface into the container that should You may also bind an existing object instance into the container using the `instance` method. The given instance will always be returned on subsequent calls into the container: - $api = new \HelpSpot\API(new HttpClient); + use App\Services\Transistor; + use App\Services\PodcastParser; - $this->app->instance('HelpSpot\API', $api); + $service = new Transistor(new PodcastParser); + + $this->app->instance(Transistor::class, $service); ### Binding Interfaces To Implementations A very powerful feature of the service container is its ability to bind an interface to a given implementation. For example, let's assume we have an `EventPusher` interface and a `RedisEventPusher` implementation. Once we have coded our `RedisEventPusher` implementation of this interface, we can register it with the service container like so: - $this->app->bind( - 'App\Contracts\EventPusher', - 'App\Services\RedisEventPusher' - ); + use App\Contrats\EventPusher; + use App\Services\RedisEventPusher; + + $this->app->bind(EventPusher::class, RedisEventPusher::class); This statement tells the container that it should inject the `RedisEventPusher` when a class needs an implementation of `EventPusher`. Now we can type-hint the `EventPusher` interface in the constructor of a class that is resolved by the container. Remember, controllers, event listeners, middleware, and various other types of classes within Laravel applications are always resolved using the container: @@ -132,7 +142,7 @@ This statement tells the container that it should inject the `RedisEventPusher` /** * Create a new class instance. * - * @param EventPusher $pusher + * @param \App\Contracts\EventPusher $pusher * @return void */ public function __construct(EventPusher $pusher) @@ -183,11 +193,34 @@ Sometimes a class may depend on an array of tagged instances. Using the `giveTag Occasionally you may have a class that receives an array of typed objects using a variadic constructor argument: + logger = $logger; @@ -229,22 +262,22 @@ Sometimes a class may have a variadic dependency that is type-hinted as a given ### Tagging -Occasionally, you may need to resolve all of a certain "category" of binding. For example, perhaps you are building a report aggregator that receives an array of many different `Report` interface implementations. After registering the `Report` implementations, you can assign them a tag using the `tag` method: +Occasionally, you may need to resolve all of a certain "category" of binding. For example, perhaps you are building a report analyzer that receives an array of many different `Report` interface implementations. After registering the `Report` implementations, you can assign them a tag using the `tag` method: - $this->app->bind('SpeedReport', function () { + $this->app->bind(CpuReport::class, function () { // }); - $this->app->bind('MemoryReport', function () { + $this->app->bind(MemoryReport::class, function () { // }); - $this->app->tag(['SpeedReport', 'MemoryReport'], 'reports'); + $this->app->tag([CpuReport::class, MemoryReport::class], 'reports'); -Once the services have been tagged, you may easily resolve them all via the `tagged` method: +Once the services have been tagged, you may easily resolve them all via the container's `tagged` method: - $this->app->bind('ReportAggregator', function ($app) { - return new ReportAggregator($app->tagged('reports')); + $this->app->bind(ReportAnalyzer::class, function ($app) { + return new ReportAnalyzer($app->tagged('reports')); }); @@ -264,19 +297,23 @@ The `extend` method allows the modification of resolved services. For example, w You may use the `make` method to resolve a class instance from the container. The `make` method accepts the name of the class or interface you wish to resolve: - $api = $this->app->make('HelpSpot\API'); + use App\Services\Transistor; + + $api = $this->app->make(Transistor::class); If you are outside of a service provider in a location of your code that does not have access to the `$app` variable, you may use the `App` [facade](/docs/{{version}}/facades) to resolve a class instance from the container: + use App\Services\Transistor; use Illuminate\Support\Facades\App; - $api = App::make('HelpSpot\API'); + $api = App::make(Transistor::class); If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the `makeWith` method. For example, we may manually pass the `$id` constructor argument required by the `HelpSpot\API` service: + use App\Services\Transistor; use Illuminate\Support\Facades\App; - $api = App::makeWith('HelpSpot\API', ['id' => 1]); + $api = App::makeWith(Transistor::class, ['id' => 1]); If you would like to have the Laravel container instance itself injected into a class that is being resolved by the container, you may type-hint the `Illuminate\Container\Container` class on your class' constructor: @@ -343,14 +380,16 @@ For example, you may type-hint a repository defined by your application in a con The service container fires an event each time it resolves an object. You may listen to this event using the `resolving` method: - $this->app->resolving(function ($object, $app) { - // Called when container resolves object of any type... - }); + use App\Services\Transistor; - $this->app->resolving(\HelpSpot\API::class, function ($api, $app) { + $this->app->resolving(Transistor::class, function ($api, $app) { // Called when container resolves objects of type "HelpSpot\API"... }); + $this->app->resolving(function ($object, $app) { + // Called when container resolves object of any type... + }); + As you can see, the object being resolved will be passed to the callback, allowing you to set any additional properties on the object before it is given to its consumer. @@ -358,10 +397,11 @@ As you can see, the object being resolved will be passed to the callback, allowi Laravel's service container implements the [PSR-11](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md) interface. Therefore, you may type-hint the PSR-11 container interface to obtain an instance of the Laravel container: + use App\Services\Transistor; use Psr\Container\ContainerInterface; Route::get('/', function (ContainerInterface $container) { - $service = $container->get('Service'); + $service = $container->get(Transistor::class); // }); From b62d1d46250495b402cae61e56c510b2e4781c5f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 15:13:09 -0600 Subject: [PATCH 034/274] augment container docs --- container.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/container.md b/container.md index 1d89c80c94..c9b2a6df91 100644 --- a/container.md +++ b/container.md @@ -1,6 +1,8 @@ # Service Container - [Introduction](#introduction) + - [Zero Configuration Resolution](#zero-configuration-resolution) + - [When To Use The Container](#when-to-use-the-container) - [Binding](#binding) - [Binding Basics](#binding-basics) - [Binding Interfaces To Implementations](#binding-interfaces-to-implementations) @@ -68,6 +70,41 @@ In this example, the `UserController` needs to retrieve users from a data source A deep understanding of the Laravel service container is essential to building a powerful, large application, as well as for contributing to the Laravel core itself. + +### Zero Configuration Resolution + +If a class has no dependencies or only depends on other concrete classes (not interfaces), the container does not need to be instructed on how to resolve that class. For example, you may place the following code in your `routes/web.php` file: + + +### When To Use The Container + +When building a Laravel application, you will often type-hint dependencies on routes, controllers, event listeners, and elsewhere without ever manually interacting with the container. For example, you might type-hint the `Illuminate\Http\Request` object on your route definition so that you can easily access the current request. Even though we never have to interact with the container to write this code, it is behind the scenes managing the injection of these dependencies: + + use Illuminate\Http\Request; + + Route::get('/', function (Request $request) { + // ... + }); + +In many cases, thanks for automatic dependency injection and [facades](/docs/{{version}}/facades), you can build Laravel applications without **ever** manually binding or resolving anything from the container. **So, when would you ever manually interact with the container?** Let's examine two situations. + +First, if you write a class that implements an interface and you wish to type-hint that interface on a route or class constructor, you must [tell the container how to resolve that interface](#binding-interfaces-to-implementations). Secondly, if you are [writing a Laravel package](/docs/{{version}}/packages) that you plan to share with other Laravel developers, you may need to bind your package's services into the container. + ## Binding From 5fbe9f95b73e8694c7418fcb796a3ec01cb2d022 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 15:14:02 -0600 Subject: [PATCH 035/274] fixes --- container.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/container.md b/container.md index c9b2a6df91..f1c80d929f 100644 --- a/container.md +++ b/container.md @@ -93,7 +93,7 @@ Thankfully, many of the classes you will be writing when building a Laravel appl ### When To Use The Container -When building a Laravel application, you will often type-hint dependencies on routes, controllers, event listeners, and elsewhere without ever manually interacting with the container. For example, you might type-hint the `Illuminate\Http\Request` object on your route definition so that you can easily access the current request. Even though we never have to interact with the container to write this code, it is behind the scenes managing the injection of these dependencies: +When building a Laravel application, you will often type-hint dependencies on routes, controllers, event listeners, and elsewhere without ever manually interacting with the container. For example, you might type-hint the `Illuminate\Http\Request` object on your route definition so that you can easily access the current request. Even though we never have to interact with the container to write this code, it is managing the injection of these dependencies behind the scenes: use Illuminate\Http\Request; @@ -101,7 +101,7 @@ When building a Laravel application, you will often type-hint dependencies on ro // ... }); -In many cases, thanks for automatic dependency injection and [facades](/docs/{{version}}/facades), you can build Laravel applications without **ever** manually binding or resolving anything from the container. **So, when would you ever manually interact with the container?** Let's examine two situations. +In many cases, thanks to automatic dependency injection and [facades](/docs/{{version}}/facades), you can build Laravel applications without **ever** manually binding or resolving anything from the container. **So, when would you ever manually interact with the container?** Let's examine two situations. First, if you write a class that implements an interface and you wish to type-hint that interface on a route or class constructor, you must [tell the container how to resolve that interface](#binding-interfaces-to-implementations). Secondly, if you are [writing a Laravel package](/docs/{{version}}/packages) that you plan to share with other Laravel developers, you may need to bind your package's services into the container. From 5d83f4bfc9fdb8248bd210ebed9f085329ceb5ee Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 15:49:14 -0600 Subject: [PATCH 036/274] update wording --- container.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container.md b/container.md index f1c80d929f..baf54dc921 100644 --- a/container.md +++ b/container.md @@ -93,7 +93,7 @@ Thankfully, many of the classes you will be writing when building a Laravel appl ### When To Use The Container -When building a Laravel application, you will often type-hint dependencies on routes, controllers, event listeners, and elsewhere without ever manually interacting with the container. For example, you might type-hint the `Illuminate\Http\Request` object on your route definition so that you can easily access the current request. Even though we never have to interact with the container to write this code, it is managing the injection of these dependencies behind the scenes: +Thanks to zero configuration resolution, you will often type-hint dependencies on routes, controllers, event listeners, and elsewhere without ever manually interacting with the container. For example, you might type-hint the `Illuminate\Http\Request` object on your route definition so that you can easily access the current request. Even though we never have to interact with the container to write this code, it is managing the injection of these dependencies behind the scenes: use Illuminate\Http\Request; From 9615db4f6e6d71cb3168a32b0c108fb24833563b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 15:53:59 -0600 Subject: [PATCH 037/274] adjust order --- container.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/container.md b/container.md index baf54dc921..3c74fbbe1d 100644 --- a/container.md +++ b/container.md @@ -338,19 +338,18 @@ You may use the `make` method to resolve a class instance from the container. Th $api = $this->app->make(Transistor::class); -If you are outside of a service provider in a location of your code that does not have access to the `$app` variable, you may use the `App` [facade](/docs/{{version}}/facades) to resolve a class instance from the container: +If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the `makeWith` method. For example, we may manually pass the `$id` constructor argument required by the `HelpSpot\API` service: use App\Services\Transistor; - use Illuminate\Support\Facades\App; - $api = App::make(Transistor::class); + $api = $this->app->makeWith(Transistor::class, ['id' => 1]); -If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the `makeWith` method. For example, we may manually pass the `$id` constructor argument required by the `HelpSpot\API` service: +If you are outside of a service provider in a location of your code that does not have access to the `$app` variable, you may use the `App` [facade](/docs/{{version}}/facades) to resolve a class instance from the container: use App\Services\Transistor; use Illuminate\Support\Facades\App; - $api = App::makeWith(Transistor::class, ['id' => 1]); + $api = App::make(Transistor::class); If you would like to have the Laravel container instance itself injected into a class that is being resolved by the container, you may type-hint the `Illuminate\Container\Container` class on your class' constructor: From 5cfdce90a64cf251d98f198c8e72060ddea26e78 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 15:57:51 -0600 Subject: [PATCH 038/274] add note about route service provdier --- lifecycle.md | 2 ++ providers.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lifecycle.md b/lifecycle.md index 67fd360cdf..52fb84ccec 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -49,6 +49,8 @@ You may be wondering why the `register` method of every service provider is call ### Routing +One of the most important service providers in your application is the `App\Providers\RouteServiceProvider`. This service provider loads the route files containing within your application's `routes` directory. Go ahead, crack open the `RouteServiceProvider` code and take look at how it works! + Once the application has been bootstrapped and all service providers have been registered, the `Request` will be handed off to the router for dispatching. The router will dispatch the request to a route or controller, as well as run any route specific middleware. Middleware provide a convenient mechanism for filtering or examining HTTP requests entering your application. For example, Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to the login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. Some middleware are assigned to all routes within the application, like those defined in the `$middleware` property of your HTTP kernel, while some are only assigned to specific routes or route groups. You can learn more about middleware by reading the complete [middleware documentation](/docs/{{version}}/middleware). diff --git a/providers.md b/providers.md index 32468c79b9..46438db48f 100644 --- a/providers.md +++ b/providers.md @@ -18,6 +18,8 @@ If you open the `config/app.php` file included with Laravel, you will see a `pro In this overview you will learn how to write your own service providers and register them with your Laravel application. +> {tip} If you would like to learn more about how Laravel handles requests and works internally, check out our documentation on the Laravel [request lifecycle](/docs/{{version}}/lifecycle). + ## Writing Service Providers From dec2adf487c0bb3776ac361bc0a5b9dd8bddfbc5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 16:04:51 -0600 Subject: [PATCH 039/274] update service provider docs --- providers.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/providers.md b/providers.md index 46438db48f..b514c17095 100644 --- a/providers.md +++ b/providers.md @@ -14,7 +14,7 @@ Service providers are the central place of all Laravel application bootstrapping But, what do we mean by "bootstrapped"? In general, we mean **registering** things, including registering service container bindings, event listeners, middleware, and even routes. Service providers are the central place to configure your application. -If you open the `config/app.php` file included with Laravel, you will see a `providers` array. These are all of the service provider classes that will be loaded for your application. Note that many of these are "deferred" providers, meaning they will not be loaded on every request, but only when the services they provide are actually needed. +If you open the `config/app.php` file included with Laravel, you will see a `providers` array. These are all of the service provider classes that will be loaded for your application. By default, a set of Laravel core service providers are listed in this array. These providers bootstrap the core Laravel components, such as the mailer, queue, cache, and others. Note that many of these are "deferred" providers, meaning they will not be loaded on every request, but only when the services they provide are actually needed. In this overview you will learn how to write your own service providers and register them with your Laravel application. @@ -40,8 +40,8 @@ Let's take a look at a basic service provider. Within any of your service provid namespace App\Providers; + use App\Services\Riak\Connection; use Illuminate\Support\ServiceProvider; - use Riak\Connection; class RiakServiceProvider extends ServiceProvider { @@ -58,7 +58,7 @@ Let's take a look at a basic service provider. Within any of your service provid } } -This service provider only defines a `register` method, and uses that method to define an implementation of `Riak\Connection` in the service container. If you don't understand how the service container works, check out [its documentation](/docs/{{version}}/container). +This service provider only defines a `register` method, and uses that method to define an implementation of `App\Services\Riak\Connection` in the service container. If you you're not yet familiar with Laravel's service container, check out [its documentation](/docs/{{version}}/container). #### The `bindings` And `singletons` Properties @@ -107,6 +107,7 @@ So, what if we need to register a [view composer](/docs/{{version}}/views#view-c namespace App\Providers; + use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; class ComposerServiceProvider extends ServiceProvider @@ -118,7 +119,7 @@ So, what if we need to register a [view composer](/docs/{{version}}/views#view-c */ public function boot() { - view()->composer('view', function () { + View::composer('view', function () { // }); } @@ -131,9 +132,15 @@ You may type-hint dependencies for your service provider's `boot` method. The [s use Illuminate\Contracts\Routing\ResponseFactory; + /** + * Bootstrap any application services. + * + * @param \Illuminate\Contracts\Routing\ResponseFactory + * @return void + */ public function boot(ResponseFactory $response) { - $response->macro('caps', function ($value) { + $response->macro('serialized', function ($value) { // }); } @@ -164,9 +171,9 @@ To defer the loading of a provider, implement the `\Illuminate\Contracts\Support namespace App\Providers; + use App\Services\Riak\Connection; use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Support\ServiceProvider; - use Riak\Connection; class RiakServiceProvider extends ServiceProvider implements DeferrableProvider { From 7186d98269819d8d6db9c2933e178ee634fdc96a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 16:36:49 -0600 Subject: [PATCH 040/274] work on facade docs --- facades.md | 37 +++++++++++++++++++++++++++++-------- installation.md | 10 +++++++++- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/facades.md b/facades.md index 70a9ed905d..91eec9291d 100644 --- a/facades.md +++ b/facades.md @@ -11,7 +11,9 @@ ## Introduction -Facades provide a "static" interface to classes that are available in the application's [service container](/docs/{{version}}/container). Laravel ships with many facades which provide access to almost all of Laravel's features. Laravel facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods. +Throughout the Laravel documentation, you will see examples of code that interacts with Laravel's features via "facades". Facades provide a "static" interface to classes that are available in the application's [service container](/docs/{{version}}/container). Laravel ships with many facades which provide access to almost all of Laravel's features. + +Laravel facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods. All of Laravel's facades are defined in the `Illuminate\Support\Facades` namespace. So, we can easily access a facade like so: @@ -23,14 +25,33 @@ All of Laravel's facades are defined in the `Illuminate\Support\Facades` namespa Throughout the Laravel documentation, many of the examples will use facades to demonstrate various features of the framework. + +#### Helper Functions + +To compliment facades, Laravel offers a variety of global "helper functions" that make it even easier to interact with common Laravel features. Some of the common helper functions you may interact with are `view`, `response`, `url`, `config`, and more. + +For example, instead of using the `Illuminate\Support\Facades\Response` facade to generate a JSON response, we may simply use the `response` function. Because helper functions are globally available, you do not need to import any classes in order to use them. + + use Illuminate\Support\Facades\Response; + + Route::get('/users', function () { + return Response::json([ + // ... + ]); + }); + + Route::get('/users', function () { + return response()->json([ + // ... + ]); + }); + ## When To Use Facades 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 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. +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. If your class is getting too large, consider splitting it into multiple smaller classes. ### Facades Vs. Dependency Injection @@ -45,7 +66,7 @@ Typically, it would not be possible to mock or stub a truly static class method. return Cache::get('key'); }); -We can write the following test to verify that the `Cache::get` method was called with the argument we expected: +Using Laravel's facade testing methods, we can write the following test to verify that the `Cache::get` method was called with the argument we expected: use Illuminate\Support\Facades\Cache; @@ -70,7 +91,7 @@ We can write the following test to verify that the `Cache::get` method was calle In addition to facades, Laravel includes a variety of "helper" functions which can perform common tasks like generating views, firing events, dispatching jobs, or sending HTTP responses. Many of these helper functions perform the same function as a corresponding facade. For example, this facade call and helper call are equivalent: - return View::make('profile'); + return Illuminate\Support\Facades\View::make('profile'); return view('profile'); @@ -105,7 +126,7 @@ Under the hood, the `cache` helper is going to call the `get` method on the clas In a Laravel application, a facade is a class that provides access to an object from the container. The machinery that makes this work is in the `Facade` class. Laravel's facades, and any custom facades you create, will extend the base `Illuminate\Support\Facades\Facade` class. -The `Facade` base class makes use of the `__callStatic()` magic-method to defer calls from your facade to an object resolved from the container. In the example below, a call is made to the Laravel cache system. By glancing at this code, one might assume that the static method `get` is being called on the `Cache` class: +The `Facade` base class makes use of the `__callStatic()` magic-method to defer calls from your facade to an object resolved from the container. In the example below, a call is made to the Laravel cache system. By glancing at this code, one might assume that the static `get` method is being called on the `Cache` class: ## Real-Time Facades -Using real-time facades, you may treat any class in your application as if it were a facade. To illustrate how this can be used, let's examine an alternative. For example, let's assume our `Podcast` model has a `publish` method. However, in order to publish the podcast, we need to inject a `Publisher` instance: +Using real-time facades, you may treat any class in your application as if it were a facade. To illustrate how this can be used, let's first examine some code that does not use real-time facades. For example, let's assume our `Podcast` model has a `publish` method. However, in order to publish the podcast, we need to inject a `Publisher` instance: ## Next Steps -Now that you have created your Laravel project, you may be wondering what to learn next. First, we strongly recommend becoming familiar with how Laravel works by reading our documentation on Laravel's [request lifecycle](/docs/{{version}}/lifecycle), [configuration](/docs/{{version}}/configuration), or [directory structure](/docs/{{version}}/structure). In addition, you should read the documentation on Laravel's [service container](/docs/{{version}}/container) and [facades](/docs/{{version}}/facades). +Now that you have created your Laravel project, you may be wondering what to learn next. First, we strongly recommend becoming familiar with how Laravel works by reading the following documentation: + +
+- [Request Lifecycle](/docs/{{version}}/lifecycle) +- [Configuration](/docs/{{version}}/configuration) +- [Directory Structure](/docs/{{version}}/structure) +- [Service Container](/docs/{{version}}/container) +- [Facades](/docs/{{version}}/facades) +
How you want to use Laravel will also dictate the next steps on your journey. There are a variety of ways to use Laravel, and we'll explore two primary use cases for the framework below. From 0ddab5c0d891b3fd3a4342dff005f5e3746e2d7a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 16:37:41 -0600 Subject: [PATCH 041/274] remove outdated note --- contracts.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts.md b/contracts.md index 36836ff613..68f46f288b 100644 --- a/contracts.md +++ b/contracts.md @@ -24,8 +24,6 @@ Laravel's [facades](/docs/{{version}}/facades) and helper functions provide a si Unlike facades, which do not require you to require them in your class' constructor, contracts allow you to define explicit dependencies for your classes. Some developers prefer to explicitly define their dependencies in this way and therefore prefer to use contracts, while other developers enjoy the convenience of facades. -> {tip} Most applications will be fine regardless of whether you prefer facades or contracts. However, if you are building a package, you should strongly consider using contracts since they will be easier to test in a package context. - ## When To Use Contracts From bcc4d8ad17559547d7bd6c7eeec7373a22f2d981 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 16:46:08 -0600 Subject: [PATCH 042/274] adjust location of some docs --- contracts.md | 46 ++++++++++++++++++++++------------------------ documentation.md | 4 ++-- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/contracts.md b/contracts.md index 68f46f288b..b3fb4bac28 100644 --- a/contracts.md +++ b/contracts.md @@ -4,14 +4,13 @@ - [Contracts Vs. Facades](#contracts-vs-facades) - [When To Use Contracts](#when-to-use-contracts) - [Loose Coupling](#loose-coupling) - - [Simplicity](#simplicity) - [How To Use Contracts](#how-to-use-contracts) - [Contract Reference](#contract-reference) ## Introduction -Laravel's Contracts are a set of interfaces that define the core services provided by the framework. For example, an `Illuminate\Contracts\Queue\Queue` contract defines the methods needed for queueing jobs, while the `Illuminate\Contracts\Mail\Mailer` contract defines the methods needed for sending e-mail. +Laravel's "contracts" are a set of interfaces that define the core services provided by the framework. For example, an `Illuminate\Contracts\Queue\Queue` contract defines the methods needed for queueing jobs, while the `Illuminate\Contracts\Mail\Mailer` contract defines the methods needed for sending e-mail. Each contract has a corresponding implementation provided by the framework. For example, Laravel provides a queue implementation with a variety of drivers, and a mailer implementation that is powered by [SwiftMailer](https://swiftmailer.symfony.com/). @@ -22,14 +21,14 @@ All of the Laravel contracts live in [their own GitHub repository](https://githu Laravel's [facades](/docs/{{version}}/facades) and helper functions provide a simple way of utilizing Laravel's services without needing to type-hint and resolve contracts out of the service container. In most cases, each facade has an equivalent contract. -Unlike facades, which do not require you to require them in your class' constructor, contracts allow you to define explicit dependencies for your classes. Some developers prefer to explicitly define their dependencies in this way and therefore prefer to use contracts, while other developers enjoy the convenience of facades. +Unlike facades, which do not require you to require them in your class' constructor, contracts allow you to define explicit dependencies for your classes. Some developers prefer to explicitly define their dependencies in this way and therefore prefer to use contracts, while other developers enjoy the convenience of facades. **In general, most applications can use facades without issue during development.** ## When To Use Contracts 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 distill the reasons for using interfaces to the following headings: loose coupling and simplicity. +However, you may still have several questions regarding the benefits of 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 @@ -38,31 +37,35 @@ First, let's review some code that is tightly coupled to a cache implementation. cache = $cache; } /** - * Retrieve an Order by ID. + * Retrieve an order by ID. * * @param int $id - * @return Order + * @return \App\Models\Order */ public function find($id) { @@ -72,11 +75,9 @@ First, let's review some code that is tightly coupled to a cache implementation. } } -In this class, the code is tightly coupled to a given cache implementation. It is tightly coupled because we are depending on a concrete Cache class from a package vendor. If the API of that package changes our code must change as well. - -Likewise, if we want to replace our underlying cache technology (Memcached) with another technology (Redis), we again will have to modify our repository. Our repository should not have so much knowledge regarding who is providing them data or how they are providing it. +In this class, the code is tightly coupled to a given cache implementation. It is tightly coupled because we are depending on a concrete cache class from a package vendor. If the API of that package changes our code must change as well. -**Instead of this approach, we can improve our code by depending on a simple, vendor agnostic interface:** +Likewise, if we want to replace our underlying cache technology (Memcached) with another technology (Redis), we again will have to modify our repository. Our repository should not have so much knowledge regarding who is providing them data or how they are providing it. Instead of this approach, we can improve our code by depending on a simple, vendor agnostic interface: -### Simplicity - -When all of Laravel's services are neatly defined within simple interfaces, it is very easy to determine the functionality offered by a given service. **The contracts serve as succinct documentation to the framework's features.** - -In addition, when you depend on simple interfaces, your code is easier to understand and maintain. Rather than tracking down which methods are available to you within a large, complicated class, you can refer to a simple, clean interface. - ## How To Use Contracts @@ -133,13 +129,15 @@ For example, take a look at this event listener: { /** * The Redis factory implementation. + * + * @var \Illuminate\Contracts\Redis\Factory */ protected $redis; /** * Create a new event handler instance. * - * @param Factory $redis + * @param \Illuminate\Contracts\Redis\Factory $redis * @return void */ public function __construct(Factory $redis) @@ -150,7 +148,7 @@ For example, take a look at this event listener: /** * Handle the event. * - * @param OrderWasPlaced $event + * @param \App\Events\OrderWasPlaced $event * @return void */ public function handle(OrderWasPlaced $event) diff --git a/documentation.md b/documentation.md index 39668ca627..7a983b6479 100644 --- a/documentation.md +++ b/documentation.md @@ -4,16 +4,15 @@ - [Contribution Guide](/docs/{{version}}/contributions) - ## Getting Started - [Installation](/docs/{{version}}/installation) - - [Request Lifecycle](/docs/{{version}}/lifecycle) - [Configuration](/docs/{{version}}/configuration) - [Directory Structure](/docs/{{version}}/structure) - [Starter Kits](/docs/{{version}}/starter-kits) - [Deployment](/docs/{{version}}/deployment) - ## Architecture Concepts + - [Request Lifecycle](/docs/{{version}}/lifecycle) - [Service Container](/docs/{{version}}/container) - [Service Providers](/docs/{{version}}/providers) - [Facades](/docs/{{version}}/facades) - - [Contracts](/docs/{{version}}/contracts) - ## The Basics - [Routing](/docs/{{version}}/routing) - [Middleware](/docs/{{version}}/middleware) @@ -43,6 +42,7 @@ - [Broadcasting](/docs/{{version}}/broadcasting) - [Cache](/docs/{{version}}/cache) - [Collections](/docs/{{version}}/collections) + - [Contracts](/docs/{{version}}/contracts) - [Events](/docs/{{version}}/events) - [File Storage](/docs/{{version}}/filesystem) - [Helpers](/docs/{{version}}/helpers) From 33bec6830eaa500d8fb6ba85e838c30249110e16 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 11 Nov 2020 16:51:05 -0600 Subject: [PATCH 043/274] add note on providers --- lifecycle.md | 6 ++++-- providers.md | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lifecycle.md b/lifecycle.md index 52fb84ccec..958f301765 100644 --- a/lifecycle.md +++ b/lifecycle.md @@ -40,9 +40,11 @@ The method signature for the HTTP kernel's `handle` method is quite simple: it r ### Service Providers -One of the most important kernel bootstrapping actions is loading the [service providers](/docs/{{version}}/providers) for your application. All of the service providers for the application are configured in the `config/app.php` configuration file's `providers` array. First, the `register` method will be called on all providers, then, once all providers have been registered, the `boot` method will be called. +One of the most important kernel bootstrapping actions is loading the [service providers](/docs/{{version}}/providers) for your application. All of the service providers for the application are configured in the `config/app.php` configuration file's `providers` array. -Service providers are responsible for bootstrapping all of the framework's various components, such as the database, queue, validation, and routing components. Since they bootstrap and configure every feature offered by the framework, service providers are the most important aspect of the entire Laravel bootstrap process. +Laravel will iterate through this list of providers and instantiate each of them. After instantiating the providers, the `register` method will be called on all of the providers. Then, once all of the providers have been registered, the `boot` method will be called on each provider. + +Service providers are responsible for bootstrapping all of the framework's various components, such as the database, queue, validation, and routing components. Essentially every major feature offered by Laravel is bootstrapped and configured by a service provider. Since they bootstrap and configure so many features offered by the framework, service providers are the most important aspect of the entire Laravel bootstrap process. You may be wondering why the `register` method of every service provider is called before calling the `boot` method on any service providers. The answer is simple. By calling the `register` method of every service provider first, service providers may depend on every container binding being registered and available by the time the `boot` method is executed. diff --git a/providers.md b/providers.md index b514c17095..43d61f38ca 100644 --- a/providers.md +++ b/providers.md @@ -14,7 +14,7 @@ Service providers are the central place of all Laravel application bootstrapping But, what do we mean by "bootstrapped"? In general, we mean **registering** things, including registering service container bindings, event listeners, middleware, and even routes. Service providers are the central place to configure your application. -If you open the `config/app.php` file included with Laravel, you will see a `providers` array. These are all of the service provider classes that will be loaded for your application. By default, a set of Laravel core service providers are listed in this array. These providers bootstrap the core Laravel components, such as the mailer, queue, cache, and others. Note that many of these are "deferred" providers, meaning they will not be loaded on every request, but only when the services they provide are actually needed. +If you open the `config/app.php` file included with Laravel, you will see a `providers` array. These are all of the service provider classes that will be loaded for your application. By default, a set of Laravel core service providers are listed in this array. These providers bootstrap the core Laravel components, such as the mailer, queue, cache, and others. Many of these providers are "deferred" providers, meaning they will not be loaded on every request, but only when the services they provide are actually needed. In this overview you will learn how to write your own service providers and register them with your Laravel application. From bd25d62135b395b82a574a4eff015422dda616d0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 09:06:34 -0600 Subject: [PATCH 044/274] update contract docs --- contracts.md | 85 ++-------------------------------------------------- 1 file changed, 3 insertions(+), 82 deletions(-) diff --git a/contracts.md b/contracts.md index b3fb4bac28..0bf01289b2 100644 --- a/contracts.md +++ b/contracts.md @@ -3,7 +3,6 @@ - [Introduction](#introduction) - [Contracts Vs. Facades](#contracts-vs-facades) - [When To Use Contracts](#when-to-use-contracts) - - [Loose Coupling](#loose-coupling) - [How To Use Contracts](#how-to-use-contracts) - [Contract Reference](#contract-reference) @@ -14,7 +13,7 @@ Laravel's "contracts" are a set of interfaces that define the core services prov Each contract has a corresponding implementation provided by the framework. For example, Laravel provides a queue implementation with a variety of drivers, and a mailer implementation that is powered by [SwiftMailer](https://swiftmailer.symfony.com/). -All of the Laravel contracts live in [their own GitHub repository](https://github.com/illuminate/contracts). This provides a quick reference point for all available contracts, as well as a single, decoupled package that may be utilized by package developers. +All of the Laravel contracts live in [their own GitHub repository](https://github.com/illuminate/contracts). This provides a quick reference point for all available contracts, as well as a single, decoupled package that may be utilized when building packages that interact with Laravel services. ### Contracts Vs. Facades @@ -26,87 +25,9 @@ Unlike facades, which do not require you to require them in your class' construc ## When To Use Contracts -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. +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. Contracts and facades are not mutually exclusive. Some parts of your applications may use facades while others depend on contracts. 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 the benefits of 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 - -First, let's review some code that is tightly coupled to a cache implementation. Consider the following: - - cache = $cache; - } - - /** - * Retrieve an order by ID. - * - * @param int $id - * @return \App\Models\Order - */ - public function find($id) - { - if ($this->cache->has($id)) { - // - } - } - } - -In this class, the code is tightly coupled to a given cache implementation. It is tightly coupled because we are depending on a concrete cache class from a package vendor. If the API of that package changes our code must change as well. - -Likewise, if we want to replace our underlying cache technology (Memcached) with another technology (Redis), we again will have to modify our repository. Our repository should not have so much knowledge regarding who is providing them data or how they are providing it. Instead of this approach, we can improve our code by depending on a simple, vendor agnostic interface: - - cache = $cache; - } - } - -Now the code is not coupled to any specific vendor, or even Laravel. Since the contracts package contains no implementation and no dependencies, you may easily write an alternative implementation of any given contract, allowing you to replace your cache implementation without modifying any of your cache consuming code. +In general, most applications can use facades without issue during development. If you are building a package that integrates with multiple PHP frameworks you may wish to use the `illuminate/contracts` package to define your integration with Laravel's services without the need to require Laravel's concrete implementations in your package's `composer.json` file. ## How To Use Contracts From 0574b2bc15815400f986d07308727725c0df2217 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 10:19:13 -0600 Subject: [PATCH 045/274] add helper link --- facades.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/facades.md b/facades.md index 91eec9291d..8a1ee8f97a 100644 --- a/facades.md +++ b/facades.md @@ -28,9 +28,9 @@ Throughout the Laravel documentation, many of the examples will use facades to d #### Helper Functions -To compliment facades, Laravel offers a variety of global "helper functions" that make it even easier to interact with common Laravel features. Some of the common helper functions you may interact with are `view`, `response`, `url`, `config`, and more. +To compliment facades, Laravel offers a variety of global "helper functions" that make it even easier to interact with common Laravel features. Some of the common helper functions you may interact with are `view`, `response`, `url`, `config`, and more. Each helper function offered by Laravel is documented with their corresponding feature; however, a complete list is available within the dedicated [helper documentation](/docs/{{version}}/helpers). -For example, instead of using the `Illuminate\Support\Facades\Response` facade to generate a JSON response, we may simply use the `response` function. Because helper functions are globally available, you do not need to import any classes in order to use them. +For example, instead of using the `Illuminate\Support\Facades\Response` facade to generate a JSON response, we may simply use the `response` function. Because helper functions are globally available, you do not need to import any classes in order to use them: use Illuminate\Support\Facades\Response; From 00ad3bce120cd8a36b279e685c2c3e4d8564ece3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 10:40:39 -0600 Subject: [PATCH 046/274] wip --- facades.md | 2 +- routing.md | 41 ++++++++++++++++++++++++----------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/facades.md b/facades.md index 8a1ee8f97a..a74a49e82c 100644 --- a/facades.md +++ b/facades.md @@ -13,7 +13,7 @@ Throughout the Laravel documentation, you will see examples of code that interacts with Laravel's features via "facades". Facades provide a "static" interface to classes that are available in the application's [service container](/docs/{{version}}/container). Laravel ships with many facades which provide access to almost all of Laravel's features. -Laravel facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods. +Laravel facades serve as "static proxies" to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods. It's perfectly fine if you don't totally understand how facades work under the hood - just go with the flow and continue learning about Laravel. All of Laravel's facades are defined in the `Illuminate\Support\Facades` namespace. So, we can easily access a facade like so: diff --git a/routing.md b/routing.md index ca2c33fa34..cfd0330e31 100644 --- a/routing.md +++ b/routing.md @@ -27,18 +27,20 @@ ## Basic Routing -The most basic Laravel routes accept a URI and a `Closure`, providing a very simple and expressive method of defining routes: +The most basic Laravel routes accept a URI and a `Closure`, providing a very simple and expressive method of defining routes and behavior without complicated routing configuration files: - Route::get('foo', function () { + use Illuminate\Support\Facades\Route; + + Route::get('/greeting', function () { return 'Hello World'; }); #### The Default Route Files -All Laravel routes are defined in your route files, which are located in the `routes` directory. These files are automatically loaded by the framework. The `routes/web.php` file defines routes that are for your web interface. These routes are assigned the `web` middleware group, which provides features like session state and CSRF protection. The routes in `routes/api.php` are stateless and are assigned the `api` middleware group. +All Laravel routes are defined in your route files, which are located in the `routes` directory. These files are automatically loaded by your application's `App\Providers\RouteServiceProvider`. The `routes/web.php` file defines routes that are for your web interface. These routes are assigned the `web` middleware group, which provides features like session state and CSRF protection. The routes in `routes/api.php` are stateless and are assigned the `api` middleware group. -For most applications, you will begin by defining routes in your `routes/web.php` file. The routes defined in `routes/web.php` may be accessed by entering the defined route's URL in your browser. For example, you may access the following route by navigating to `http://your-app.test/user` in your browser: +For most applications, you will begin by defining routes in your `routes/web.php` file. The routes defined in `routes/web.php` may be accessed by entering the defined route's URL in your browser. For example, you may access the following route by navigating to `http://example.com/user` in your browser: use App\Http\Controllers\UserController; @@ -71,7 +73,7 @@ Sometimes you may need to register a route that responds to multiple HTTP verbs. #### CSRF Protection -Any HTML forms pointing to `POST`, `PUT`, `PATCH`, or `DELETE` routes that are defined in the `web` routes file should include a CSRF token field. Otherwise, the request will be rejected. You can read more about CSRF protection in the [CSRF documentation](/docs/{{version}}/csrf): +Remember, any HTML forms pointing to `POST`, `PUT`, `PATCH`, or `DELETE` routes that are defined in the `web` routes file should include a CSRF token field. Otherwise, the request will be rejected. You can read more about CSRF protection in the [CSRF documentation](/docs/{{version}}/csrf):
@csrf @@ -89,7 +91,7 @@ By default, `Route::redirect` returns a `302` status code. You may customize the Route::redirect('/here', '/there', 301); -You may use the `Route::permanentRedirect` method to return a `301` status code: +Or, you may use the `Route::permanentRedirect` method to return a `301` status code: Route::permanentRedirect('/here', '/there'); @@ -98,7 +100,7 @@ You may use the `Route::permanentRedirect` method to return a `301` status code: ### View Routes -If your route only needs to return a view, you may use the `Route::view` method. Like the `redirect` method, this method provides a simple shortcut so that you do not have to define a full route or controller. The `view` method accepts a URI as its first argument and a view name as its second argument. In addition, you may provide an array of data to pass to the view as an optional third argument: +If your route only needs to return a [view](/docs/{{version}}/views), you may use the `Route::view` method. Like the `redirect` method, this method provides a simple shortcut so that you do not have to define a full route or controller. The `view` method accepts a URI as its first argument and a view name as its second argument. In addition, you may provide an array of data to pass to the view as an optional third argument: Route::view('/welcome', 'welcome'); @@ -124,12 +126,12 @@ You may define as many route parameters as required by your route: // }); -Route parameters are always encased within `{}` braces and should consist of alphabetic characters, and may not contain a `-` character. Instead of using the `-` character, use an underscore (`_`). Route parameters are injected into route callbacks / controllers based on their order - the names of the callback / controller arguments do not matter. +Route parameters are always encased within `{}` braces and should consist of alphabetic characters. Underscores (`_`) are also acceptable within route parameter names. Route parameters are injected into route callbacks / controllers based on their order - the names of the route callback / controller arguments do not matter. ### Optional Parameters -Occasionally you may need to specify a route parameter, but make the presence of that route parameter optional. You may do so by placing a `?` mark after the parameter name. Make sure to give the route's corresponding variable a default value: +Occasionally you may need to specify a route parameter that may not always be present in the URI. You may do so by placing a `?` mark after the parameter name. Make sure to give the route's corresponding variable a default value: Route::get('user/{name?}', function ($name = null) { return $name; @@ -166,10 +168,12 @@ For convenience, some commonly used regular expression patterns have helper meth // })->whereUuid('id'); +If the incoming request does not match the route pattern constraints, a `404` HTTP response will be returned. + #### 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`: +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 `App\Providers\RouteServiceProvider` class: /** * Define your route model bindings, pattern filters, etc. @@ -183,16 +187,16 @@ If you would like a route parameter to always be constrained by a given regular Once the pattern has been defined, it is automatically applied to all routes using that parameter name: - Route::get('user/{id}', function ($id) { + Route::get('/user/{id}', function ($id) { // Only executed if {id} is numeric... }); #### Encoded Forward Slashes -The Laravel routing component allows all characters except `/`. You must explicitly allow `/` to be part of your placeholder using a `where` condition regular expression: +The Laravel routing component allows all characters except `/` to be present within route parameter values. You must explicitly allow `/` to be part of your placeholder using a `where` condition regular expression: - Route::get('search/{search}', function ($search) { + Route::get('/search/{search}', function ($search) { return $search; })->where('search', '.*'); @@ -203,20 +207,23 @@ The Laravel routing component allows all characters except `/`. You must explici Named routes allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route by chaining the `name` method onto the route definition: - Route::get('user/profile', function () { + Route::get('/user/profile', function () { // })->name('profile'); You may also specify route names for controller actions: - Route::get('user/profile', [UserProfileController::class, 'show'])->name('profile'); + Route::get( + '/user/profile', + [UserProfileController::class, 'show'] + )->name('profile'); > {note} Route names should always be unique. #### Generating URLs To Named Routes -Once you have assigned a name to a given route, you may use the route's name when generating URLs or redirects via the global `route` function: +Once you have assigned a name to a given route, you may use the route's name when generating URLs or redirects via Laravel's `route` and `redirect` helper functions: // Generating URLs... $url = route('profile'); @@ -224,7 +231,7 @@ Once you have assigned a name to a given route, you may use the route's name whe // Generating Redirects... return redirect()->route('profile'); -If the named route defines parameters, you may pass the parameters as the second argument to the `route` function. The given parameters will automatically be inserted into the URL in their correct positions: +If the named route defines parameters, you may pass the parameters as the second argument to the `route` function. The given parameters will automatically be inserted into the generated URL in their correct positions: Route::get('user/{id}/profile', function ($id) { // From 77660a36aa1ab337211ec9659a631f1ba76009d6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 10:50:55 -0600 Subject: [PATCH 047/274] wip --- routing.md | 58 +++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/routing.md b/routing.md index cfd0330e31..85a1aa570a 100644 --- a/routing.md +++ b/routing.md @@ -233,7 +233,7 @@ Once you have assigned a name to a given route, you may use the route's name whe If the named route defines parameters, you may pass the parameters as the second argument to the `route` function. The given parameters will automatically be inserted into the generated URL in their correct positions: - Route::get('user/{id}/profile', function ($id) { + Route::get('/user/{id}/profile', function ($id) { // })->name('profile'); @@ -241,7 +241,7 @@ If the named route defines parameters, you may pass the parameters as the second If you pass additional parameters in the array, those key / value pairs will automatically be added to the generated URL's query string: - Route::get('user/{id}/profile', function ($id) { + Route::get('/user/{id}/profile', function ($id) { // })->name('profile'); @@ -282,14 +282,14 @@ Nested groups attempt to intelligently "merge" attributes with their parent grou ### Middleware -To assign middleware to all routes within a group, you may use the `middleware` method before defining the group. Middleware are executed in the order they are listed in the array: +To assign [middleware](/docs/{{version}}/middleware) to all routes within a group, you may use the `middleware` method before defining the group. Middleware are executed in the order they are listed in the array: Route::middleware(['first', 'second'])->group(function () { Route::get('/', function () { // Uses first & second middleware... }); - Route::get('user/profile', function () { + Route::get('/user/profile', function () { // Uses first & second middleware... }); }); @@ -299,7 +299,7 @@ To assign middleware to all routes within a group, you may use the `middleware` Route groups may also be used to handle subdomain routing. Subdomains may be assigned route parameters just like route URIs, allowing you to capture a portion of the subdomain for usage in your route or controller. The subdomain may be specified by calling the `domain` method before defining the group: - Route::domain('{account}.myapp.com')->group(function () { + Route::domain('{account}.example.com')->group(function () { Route::get('user/{id}', function ($account, $id) { // }); @@ -313,7 +313,7 @@ Route groups may also be used to handle subdomain routing. Subdomains may be ass The `prefix` method may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with `admin`: Route::prefix('admin')->group(function () { - Route::get('users', function () { + Route::get('/users', function () { // Matches The "/admin/users" URL }); }); @@ -324,7 +324,7 @@ The `prefix` method may be used to prefix each route in the group with a given U The `name` method may be used to prefix each route name in the group with a given string. For example, you may want to prefix all of the grouped route's names with `admin`. The given string is prefixed to the route name exactly as it is specified, so we will be sure to provide the trailing `.` character in the prefix: Route::name('admin.')->group(function () { - Route::get('users', function () { + Route::get('/users', function () { // Route assigned name "admin.users"... })->name('users'); }); @@ -332,14 +332,16 @@ The `name` method may be used to prefix each route name in the group with a give ## Route Model Binding -When injecting a model ID to a route or controller action, you will often query to retrieve the model that corresponds to that ID. Laravel route model binding provides a convenient way to automatically inject the model instances directly into your routes. For example, instead of injecting a user's ID, you can inject the entire `User` model instance that matches the given ID. +When injecting a model ID to a route or controller action, you will often query the database to retrieve the model that corresponds to that ID. Laravel route model binding provides a convenient way to automatically inject the model instances directly into your routes. For example, instead of injecting a user's ID, you can inject the entire `User` model instance that matches the given ID. ### Implicit Binding Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name. For example: - Route::get('api/users/{user}', function (App\Models\User $user) { + use App\Models\User; + + Route::get('/users/{user}', function (User $user) { return $user->email; }); @@ -350,40 +352,28 @@ Of course, implicit binding is also possible when using controller methods. Agai use App\Http\Controllers\UserController; use App\Models\User; - Route::get('users/{user}', [UserController::class, 'show']); + // Route definition... + Route::get('/users/{user}', [UserController::class, 'show']); + // Controller method definition... public function show(User $user) { return view('user.profile', ['user' => $user]); } + #### Customizing The Key Sometimes you may wish to resolve Eloquent models using a column other than `id`. To do so, you may specify the column in the route parameter definition: - Route::get('api/posts/{post:slug}', function (App\Models\Post $post) { - return $post; - }); - - -#### Custom Keys & Scoping - -Sometimes, when implicitly binding multiple Eloquent models in a single route definition, you may wish to scope the second Eloquent model such that it must be a child of the first Eloquent model. For example, consider this situation that retrieves a blog post by slug for a specific user: - use App\Models\Post; - use App\Models\User; - Route::get('api/users/{user}/posts/{post:slug}', function (User $user, Post $post) { + Route::get('/posts/{post:slug}', function (Post $post) { return $post; }); -When using a custom keyed implicit binding as a nested route parameter, Laravel will automatically scope the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent. In this case, it will be assumed that the `User` model has a relationship named `posts` (the plural of the route parameter name) which can be used to retrieve the `Post` model. - - -#### Customizing The Default Key Name - -If you would like model binding to use a default 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 always 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. @@ -395,6 +385,20 @@ If you would like model binding to use a default database column other than `id` return 'slug'; } + +#### Custom Keys & Scoping + +When implicitly binding multiple Eloquent models in a single route definition, you may wish to scope the second Eloquent model such that it must be a child of the previous Eloquent model. For example, consider this route definition that retrieves a blog post by slug for a specific user: + + use App\Models\Post; + use App\Models\User; + + Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) { + return $post; + }); + +When using a custom keyed implicit binding as a nested route parameter, Laravel will automatically scope the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent. In this case, it will be assumed that the `User` model has a relationship named `posts` (the plural form of the route parameter name) which can be used to retrieve the `Post` model. + ### Explicit Binding From 6f354432b31c8efd9a213d77f0bf128623c868c0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 10:54:56 -0600 Subject: [PATCH 048/274] wip --- routing.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/routing.md b/routing.md index 85a1aa570a..c9af0d0e60 100644 --- a/routing.md +++ b/routing.md @@ -168,7 +168,7 @@ For convenience, some commonly used regular expression patterns have helper meth // })->whereUuid('id'); -If the incoming request does not match the route pattern constraints, a `404` HTTP response will be returned. +If the incoming request does not match the route pattern constraints, a 404 HTTP response will be returned. #### Global Constraints @@ -402,7 +402,10 @@ When using a custom keyed implicit binding as a nested route parameter, Laravel ### Explicit Binding -To register an explicit binding, use the router's `model` method to specify the class for a given parameter. You should define your explicit model bindings at the beginning of the `boot` method of your `RouteServiceProvider` class: +You are not required to use Laravel's implicit, convention based model resolution in order to use model binding. You can also explicitly define how route parameters correspond to models. To register an explicit binding, use the router's `model` method to specify the class for a given parameter. You should define your explicit model bindings at the beginning of the `boot` method of your `RouteServiceProvider` class: + + use App\Models\User; + use Illuminate\Support\Facades\Route; /** * Define your route model bindings, pattern filters, etc. @@ -411,25 +414,30 @@ To register an explicit binding, use the router's `model` method to specify the */ public function boot() { - Route::model('user', \App\Models\User::class); + Route::model('user', User::class); // ... } Next, define a route that contains a `{user}` parameter: - Route::get('profile/{user}', function (App\Models\User $user) { + use App\Models\User; + + Route::get('/users/{user}', function (User $user) { // }); -Since we have bound all `{user}` parameters to the `App\Models\User` model, a `User` instance will be injected into the route. So, for example, a request to `profile/1` will inject the `User` instance from the database which has an ID of `1`. +Since we have bound all `{user}` parameters to the `App\Models\User` model, an instance of that class will be injected into the route. So, for example, a request to `users/1` will inject the `User` instance from the database which has an ID of `1`. If a matching model instance is not found in the database, a 404 HTTP response will be automatically generated. #### Customizing The Resolution Logic -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: +If you wish to define your own model binding 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. Again, this customization should take place in the `boot` method of your application's `RouteServiceProvider`: + + use App\Models\User; + use Illuminate\Support\Facades\Route; /** * Define your route model bindings, pattern filters, etc. @@ -439,7 +447,7 @@ If you wish to use your own resolution logic, you may use the `Route::bind` meth public function boot() { Route::bind('user', function ($value) { - return App\Models\User::where('name', $value)->firstOrFail(); + return User::where('name', $value)->firstOrFail(); }); // ... From 28871d8f09448faf00213efec2756f5d92681562 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 11:16:40 -0600 Subject: [PATCH 049/274] wip --- routing.md | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/routing.md b/routing.md index c9af0d0e60..0b9bd54208 100644 --- a/routing.md +++ b/routing.md @@ -485,7 +485,7 @@ If a route is utilizing [implicit binding scoping](#implicit-model-binding-scopi ## Fallback Routes -Using the `Route::fallback` method, you may define a route that will be executed when no other route matches the incoming request. Typically, unhandled requests will automatically render a "404" page via your application's exception handler. However, since you may define the `fallback` route within your `routes/web.php` file, all middleware in the `web` middleware group will apply to the route. You are free to add additional middleware to this route as needed: +Using the `Route::fallback` method, you may define a route that will be executed when no other route matches the incoming request. Typically, unhandled requests will automatically render a "404" page via your application's exception handler. However, since you would typically define the `fallback` route within your `routes/web.php` file, all middleware in the `web` middleware group will apply to the route. You are free to add additional middleware to this route as needed: Route::fallback(function () { // @@ -499,18 +499,26 @@ Using the `Route::fallback` method, you may define a route that will be executed ### Defining Rate Limiters -Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. Typically, this may be done in your application's `RouteServiceProvider`. +Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. Typically, this should be done within the `configureRateLimiting` method of your application's `App\Providers\RouteServiceProvider` class. -Rate limiters are defined using the `RateLimiter` facade's `for` method. The `for` method accepts a rate limiter name and a Closure that returns the limit configuration that should apply to routes that are assigned this rate limiter: +Rate limiters are defined using the `RateLimiter` facade's `for` method. The `for` method accepts a rate limiter name and a Closure that returns the limit configuration that should apply to routes that are assigned to the rate limiter. Limit configuration are instances of the `Illuminate\Cache\RateLimiting\Limit` class. This class contains helpful "builder" methods so that you can quickly define your limit. The rate limiter name may be any string you wish: use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Support\Facades\RateLimiter; - RateLimiter::for('global', function (Request $request) { - return Limit::perMinute(1000); - }); + /** + * Configure the rate limiters for the application. + * + * @return void + */ + protected function configureRateLimiting() + { + RateLimiter::for('global', function (Request $request) { + return Limit::perMinute(1000); + }); + } -If the incoming request exceeds the specified rate limit, a response with a 429 HTTP status code will be automatically returned by Laravel. If you would like to define your own response that should be returned by a rate limit, you may use the `response` method: +If the incoming request exceeds the specified rate limit, a response with a 429 HTTP status code will automatically be returned by Laravel. If you would like to define your own response that should be returned by a rate limit, you may use the `response` method: RateLimiter::for('global', function (Request $request) { return Limit::perMinute(1000)->response(function () { @@ -567,7 +575,7 @@ Rate limiters may be attached to routes or route groups using the `throttle` [mi #### Throttling With Redis -Typically, the `throttle` middleware is mapped to the `Illuminate\Routing\Middleware\ThrottleRequests` class. This mapping is defined in your application's HTTP kernel. However, if you are using Redis as your application's cache driver, you may wish to change this mapping to use the `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` class. This class is more efficient at managing rate limiting using Redis: +Typically, the `throttle` middleware is mapped to the `Illuminate\Routing\Middleware\ThrottleRequests` class. This mapping is defined in your application's HTTP kernel (`App\Http\Kernel`). However, if you are using Redis as your application's cache driver, you may wish to change this mapping to use the `Illuminate\Routing\Middleware\ThrottleRequestsWithRedis` class. This class is more efficient at managing rate limiting using Redis: 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class, @@ -576,14 +584,14 @@ Typically, the `throttle` middleware is mapped to the `Illuminate\Routing\Middle HTML forms do not support `PUT`, `PATCH` or `DELETE` actions. So, when defining `PUT`, `PATCH` or `DELETE` routes that are called from an HTML form, you will need to add a hidden `_method` field to the form. The value sent with the `_method` field will be used as the HTTP request method: - + -You may use the `@method` Blade directive to generate the `_method` input: +For convenience, you may use the `@method` [Blade directive](/docs/{{version}}/blade) to generate the `_method` input field: -
+ @method('PUT') @csrf
@@ -593,17 +601,17 @@ You may use the `@method` Blade directive to generate the `_method` input: You may use the `current`, `currentRouteName`, and `currentRouteAction` methods on the `Route` facade to access information about the route handling the incoming request: - $route = Route::current(); - - $name = Route::currentRouteName(); + use Illuminate\Support\Facades\Route; - $action = Route::currentRouteAction(); + $route = Route::current(); // Illuminate\Routing\Route + $name = Route::currentRouteName(); // string + $action = Route::currentRouteAction(); // string -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. +You may 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 of the methods that are available on the router and route classes. ## Cross-Origin Resource Sharing (CORS) -Laravel can automatically respond to CORS OPTIONS requests with values that you configure. All CORS settings may be configured in your `cors` configuration file and OPTIONS requests will automatically be handled by the `HandleCors` middleware that is included by default in your global middleware stack. +Laravel can automatically respond to CORS `OPTIONS` HTTP requests with values that you configure. All CORS settings may be configured in your application's `config/cors.php` configuration file. The `OPTIONS` requests will automatically be handled by the `HandleCors` [middleware](/docs/{{version}}/middleware) that is included by default in your global middleware stack. Your global middleware stack is located in your application's HTTP kernel (`App\Http\Kernel`). > {tip} For more information on CORS and CORS headers, please consult the [MDN web documentation on CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#The_HTTP_response_headers). From 67d13ac49bdc5baaa00764c1cda5add266568051 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 11:42:15 -0600 Subject: [PATCH 050/274] wip --- middleware.md | 90 ++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/middleware.md b/middleware.md index 8b2ca25f3b..f38104616a 100644 --- a/middleware.md +++ b/middleware.md @@ -13,20 +13,18 @@ ## Introduction -Middleware provide a convenient mechanism for filtering HTTP requests entering your application. For example, Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to the login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. +Middleware provide a convenient mechanism for inspecting and filtering HTTP requests entering your application. For example, Laravel includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will redirect the user to your application's login screen. However, if the user is authenticated, the middleware will allow the request to proceed further into the application. -Additional middleware can be written to perform a variety of tasks besides authentication. A CORS middleware might be responsible for adding the proper headers to all responses leaving your application. A logging middleware might log all incoming requests to your application. - -There are several middleware included in the Laravel framework, including middleware for authentication and CSRF protection. All of these middleware are located in the `app/Http/Middleware` directory. +Additional middleware can be written to perform a variety of tasks besides authentication. For example, a logging middleware might log all incoming requests to your application. There are several middleware included in the Laravel framework, including middleware for authentication and CSRF protection. All of these middleware are located in the `app/Http/Middleware` directory. ## Defining Middleware To create a new middleware, use the `make:middleware` Artisan command: - php artisan make:middleware CheckAge + php artisan make:middleware EnsureTokenIsValid -This command will place a new `CheckAge` class within your `app/Http/Middleware` directory. In this middleware, we will only allow access to the route if the supplied `age` is greater than 200. Otherwise, we will redirect the users back to the `home` URI: +This command will place a new `EnsureTokenIsValid` class within your `app/Http/Middleware` directory. In this middleware, we will only allow access to the route if the supplied `token` input matches a specified value. Otherwise, we will redirect the users back to the `home` URI: age <= 200) { + if ($request->input('token') !== 'my-secret-token') { return redirect('home'); } @@ -53,16 +51,17 @@ This command will place a new `CheckAge` class within your `app/Http/Middleware` } } -As you can see, if the given `age` is less than or equal to `200`, the middleware will return an HTTP redirect to the client; otherwise, the request will be passed further into the application. To pass the request deeper into the application (allowing the middleware to "pass"), call the `$next` callback with the `$request`. +As you can see, if the given `token` does not match our secret token, the middleware will return an HTTP redirect to the client; otherwise, the request will be passed further into the application. To pass the request deeper into the application (allowing the middleware to "pass"), you should call the `$next` callback with the `$request`. It's best to envision middleware as a series of "layers" HTTP requests must pass through before they hit your application. Each layer can examine the request and even reject it entirely. > {tip} All middleware are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a middleware's constructor. -#### Before & After Middleware + +#### Middleware & Responses -Whether a middleware runs before or after a request depends on the middleware itself. For example, the following middleware would perform some task **before** the request is handled by the application: +Of course, a middleware can perform tasks before or after passing the request deeper into the application. For example, the following middleware would perform some task **before** the request is handled by the application: ### Assigning Middleware To Routes -If you would like to assign middleware to specific routes, you should first assign the middleware a key in your `app/Http/Kernel.php` file. By default, the `$routeMiddleware` property of this class contains entries for the middleware included with Laravel. To add your own, append it to this list and assign it a key of your choosing: +If you would like to assign middleware to specific routes, you should first assign the middleware a key in your application's `app/Http/Kernel.php` file. By default, the `$routeMiddleware` property of this class contains entries for the middleware included with Laravel. You may add your own middleware to this list and assign it a key of your choosing: - // Within App\Http\Kernel Class... + // Within App\Http\Kernel class... protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, @@ -129,36 +128,36 @@ If you would like to assign middleware to specific routes, you should first assi Once the middleware has been defined in the HTTP kernel, you may use the `middleware` method to assign middleware to a route: - Route::get('admin/profile', function () { + Route::get('/profile', function () { // })->middleware('auth'); -You may also assign multiple middleware to the route: +You may assign multiple middleware to the route by passing an array of middleware names to the `middleware` method: Route::get('/', function () { // - })->middleware('first', 'second'); + })->middleware(['first', 'second']); When assigning middleware, you may also pass the fully qualified class name: - use App\Http\Middleware\CheckAge; + use App\Http\Middleware\EnsureTokenIsValid; - Route::get('admin/profile', function () { + Route::get('/profile', function () { // - })->middleware(CheckAge::class); + })->middleware(EnsureTokenIsValid::class); When assigning middleware to a group of routes, you may occasionally need to prevent the middleware from being applied to an individual route within the group. You may accomplish this using the `withoutMiddleware` method: - use App\Http\Middleware\CheckAge; + use App\Http\Middleware\EnsureTokenIsValid; - Route::middleware([CheckAge::class])->group(function () { + Route::middleware([EnsureTokenIsValid::class])->group(function () { Route::get('/', function () { // }); - Route::get('admin/profile', function () { + Route::get('/profile', function () { // - })->withoutMiddleware([CheckAge::class]); + })->withoutMiddleware([EnsureTokenIsValid::class]); }); The `withoutMiddleware` method can only remove route middleware and does not apply to [global middleware](#global-middleware). @@ -166,9 +165,9 @@ The `withoutMiddleware` method can only remove route middleware and does not app ### Middleware Groups -Sometimes you may want to group several middleware under a single key to make them easier to assign to routes. You may do this using the `$middlewareGroups` property of your HTTP kernel. +Sometimes you may want to group several middleware under a single key to make them easier to assign to routes. You may accomplish this using the `$middlewareGroups` property of your HTTP kernel. -Out of the box, Laravel comes with `web` and `api` middleware groups that contain common middleware you may want to apply to your web UI and API routes: +Out of the box, Laravel comes with `web` and `api` middleware groups that contain common middleware you may want to apply to your web and API routes. Remember, these middleware group are automatically applied by your application's `App\Providers\RouteServiceProvider` service provider to routes within your corresponding `web` and `api` route files: /** * The application's route middleware groups. @@ -197,20 +196,16 @@ Middleware groups may be assigned to routes and controller actions using the sam // })->middleware('web'); - Route::group(['middleware' => ['web']], function () { - // - }); - - Route::middleware(['web', 'subscribed'])->group(function () { + Route::middleware(['web'])->group(function () { // }); -> {tip} Out of the box, the `web` middleware group is automatically applied to your `routes/web.php` file by the `RouteServiceProvider`. +> {tip} Out of the box, the `web` and `api` middleware groups are automatically applied to your application's corresponding `routes/web.php` and `routes/api.php` files by the `App\Providers\RouteServiceProvider`. ### Sorting Middleware -Rarely, you may need your middleware to execute in a specific order but not have control over their order when they are assigned to the route. In this case, you may specify your middleware priority using the `$middlewarePriority` property of your `app/Http/Kernel.php` file: +Rarely, you may need your middleware to execute in a specific order but not have control over their order when they are assigned to the route. In this case, you may specify your middleware priority using the `$middlewarePriority` property of your `app/Http/Kernel.php` file. This property may not exist in your HTTP kernel by default. If it does not exist, you may copy its default definition below: /** * The priority-sorted list of middleware. @@ -220,6 +215,7 @@ Rarely, you may need your middleware to execute in a specific order but not have * @var array */ protected $middlewarePriority = [ + \Illuminate\Cookie\Middleware\EncryptCookies::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, @@ -232,7 +228,7 @@ Rarely, you may need your middleware to execute in a specific order but not have ## Middleware Parameters -Middleware can also receive additional parameters. For example, if your application needs to verify that the authenticated user has a given "role" before performing a given action, you could create a `CheckRole` middleware that receives a role name as an additional argument. +Middleware can also receive additional parameters. For example, if your application needs to verify that the authenticated user has a given "role" before performing a given action, you could create a `EnsureUserHasRole` middleware that receives a role name as an additional argument. Additional middleware parameters will be passed to the middleware after the `$next` argument: @@ -242,7 +238,7 @@ Additional middleware parameters will be passed to the middleware after the `$ne use Closure; - class CheckRole + class EnsureUserHasRole { /** * Handle the incoming request. @@ -265,7 +261,7 @@ Additional middleware parameters will be passed to the middleware after the `$ne Middleware parameters may be specified when defining the route by separating the middleware name and parameters with a `:`. Multiple parameters should be delimited by commas: - Route::put('post/{id}', function ($id) { + Route::put('/post/{id}', function ($id) { // })->middleware('role:editor'); @@ -280,24 +276,38 @@ Sometimes a middleware may need to do some work after the HTTP response has been use Closure; - class StartSession + class TerminatingMiddleware { + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return mixed + */ public function handle($request, Closure $next) { return $next($request); } + /** + * Handle tasks after the response has been sent to the browser. + * + * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Response $response + * @return void + */ public function terminate($request, $response) { - // Store the session data... + // ... } } The `terminate` method should receive both the request and the response. Once you have defined a terminable middleware, you should add it to the list of route or global middleware in the `app/Http/Kernel.php` file. -When calling the `terminate` method on your middleware, Laravel will resolve a fresh instance of the middleware from the [service container](/docs/{{version}}/container). If you would like to use the same middleware instance when the `handle` and `terminate` methods are called, register the middleware with the container using the container's `singleton` method. Typically this should be done in the `register` method of your `AppServiceProvider.php`: +When calling the `terminate` method on your middleware, Laravel will resolve a fresh instance of the middleware from the [service container](/docs/{{version}}/container). If you would like to use the same middleware instance when the `handle` and `terminate` methods are called, register the middleware with the container using the container's `singleton` method. Typically this should be done in the `register` method of your `AppServiceProvider`: - use App\Http\Middleware\TerminableMiddleware; + use App\Http\Middleware\TerminatingMiddleware; /** * Register any application services. @@ -306,5 +316,5 @@ When calling the `terminate` method on your middleware, Laravel will resolve a f */ public function register() { - $this->app->singleton(TerminableMiddleware::class); + $this->app->singleton(TerminatingMiddleware::class); } From c7daab2a3ee470ad5d90f841733bd054cdd76bf9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 14:02:47 -0600 Subject: [PATCH 051/274] wip --- csrf.md | 65 ++++++++++++++++++++++++++++++++++++++++++------------ sanctum.md | 14 +++++++++--- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/csrf.md b/csrf.md index 93a167bab8..7f33796065 100644 --- a/csrf.md +++ b/csrf.md @@ -1,6 +1,7 @@ # CSRF Protection - [Introduction](#csrf-introduction) +- [Preventing CSRF Requests](#preventing-csrf-requests) - [Excluding URIs](#csrf-excluding-uris) - [X-CSRF-Token](#csrf-x-csrf-token) - [X-XSRF-Token](#csrf-x-xsrf-token) @@ -8,30 +9,66 @@ ## Introduction -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. +Cross-site request forgeries are a type of malicious exploit whereby unauthorized commands are performed on behalf of an authenticated user. Thankfully, Laravel makes it easy to protect your application from [cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) (CSRF) attacks. -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. + +#### An Explanation Of The Vulnerability -Anytime you define an HTML form in your application, you should include a hidden CSRF token field in the form so that the CSRF protection middleware can validate the request. You may use the `@csrf` Blade directive to generate the token field: +In case you're not familiar with cross-site request forgeries, let's discuss an example of how this vulnerability can be exploited. Imagine your application has a `/user/email` route that accepts a `POST` request to change the authenticated user's email address. Most likely, this route expects an `email` input field to contain the email address the user would like to begin using. + +Without CSRF protection, a malicious website could create an HTML form that points to your application's `/user/email` route and submits the malicious user's own email address: + +
+ +
+ + + + If the malicious website automatically submits the form when the page is loaded, the malicious user only needs to lure an unsuspecting user of your application to visit their website and their email address will be changed in your application. + + To prevent this vulnerability, we need to inspect every incoming `POST`, `PUT`, `PATCH`, or `DELETE` request for a secret session value that the malicious application is unable to access. + + +## Preventing CSRF Requests + +Laravel automatically generates a CSRF "token" for each active [user session](/docs/{{version}}/session) managed by the application. This token is used to verify that the authenticated user is the person actually making the requests to the application. Since this token is stored in the user's session and changes each time the session is regenerated, a malicious application is unable to access it. + +The current session's CSRF token can be accessed via the request's session or via the `csrf_token` helper function: + + use Illuminate\Http\Request; + + Route::get('/token', function (Request $request) { + $token = $request->session()->token(); + + $token = csrf_token(); + + // ... + }); + +Anytime you define an HTML form in your application, you should include a hidden CSRF `_token` field in the form so that the CSRF protection middleware can validate the request. For convenience, you may use the `@csrf` Blade directive to generate the hidden token input field:
@csrf - ... + + +
-The `VerifyCsrfToken` [middleware](/docs/{{version}}/middleware), which is included in the `web` middleware group, will automatically verify that the token in the request input matches the token stored in the session. +The `App\Http\Middleware\VerifyCsrfToken` [middleware](/docs/{{version}}/middleware), which is included in the `web` middleware group by default, will automatically verify that the token in the request input matches the token stored in the session. When these two tokens match, we know that the authenticated user is the one initiating the request. - -#### CSRF Tokens & JavaScript + +### CSRF Tokens & SPAs -When building JavaScript driven applications, it is convenient to have your JavaScript HTTP library automatically attach the CSRF token to every outgoing request. By default, the Axios HTTP library provided in the `resources/js/bootstrap.js` file automatically sends an `X-XSRF-TOKEN` header using the value of the encrypted `XSRF-TOKEN` cookie. If you are not using this library, you will need to manually configure this behavior for your application. +If you are building an SPA that is utilizing Laravel as an API backend, you should consult the [Laravel Sanctum documentation](/docs/{{version}}/sanctum) for information on authenticating with your API and protecting against CSRF vulnerabilities. ## Excluding URIs From CSRF Protection Sometimes you may wish to exclude a set of URIs from CSRF protection. For example, if you are using [Stripe](https://stripe.com) to process payments and are utilizing their webhook system, you will need to exclude your Stripe webhook handler route from CSRF protection since Stripe will not know what CSRF token to send to your routes. -Typically, you should place these kinds of routes outside of the `web` middleware group that the `RouteServiceProvider` applies to all routes in the `routes/web.php` file. However, you may also exclude the routes by adding their URIs to the `$except` property of the `VerifyCsrfToken` middleware: +Typically, you should place these kinds of routes outside of the `web` middleware group that the `App\Providers\RouteServiceProvider` applies to all routes in the `routes/web.php` file. However, you may also exclude the routes by adding their URIs to the `$except` property of the `VerifyCsrfToken` middleware: {tip} The CSRF middleware is automatically disabled when [running tests](/docs/{{version}}/testing). +> {tip} For convenience, the CSRF middleware is automatically disabled for all routes when [running tests](/docs/{{version}}/testing). ## X-CSRF-TOKEN -In addition to checking for the CSRF token as a POST parameter, the `VerifyCsrfToken` middleware will also check for the `X-CSRF-TOKEN` request header. You could, for example, store the token in an HTML `meta` tag: +In addition to checking for the CSRF token as a POST parameter, the `App\Http\Middleware\VerifyCsrfToken` middleware will also check for the `X-CSRF-TOKEN` request header. You could, for example, store the token in an HTML `meta` tag: -Then, once you have created the `meta` tag, you can instruct a library like jQuery to automatically add the token to all request headers. This provides simple, convenient CSRF protection for your AJAX based applications: +Then, you can instruct a library like jQuery to automatically add the token to all request headers. This provides simple, convenient CSRF protection for your AJAX based applications using legacy JavaScript technology: $.ajaxSetup({ headers: { @@ -75,6 +112,6 @@ Then, once you have created the `meta` tag, you can instruct a library like jQue Laravel stores the current CSRF token in an encrypted `XSRF-TOKEN` cookie that is included with each response generated by the framework. You can use the cookie value to set the `X-XSRF-TOKEN` request header. -This cookie is primarily sent as a convenience since some JavaScript frameworks and libraries, like Angular and Axios, automatically place its value in the `X-XSRF-TOKEN` header on same-origin requests. +This cookie is primarily sent as a developer convenience since some JavaScript frameworks and libraries, like Angular and Axios, automatically place its value in the `X-XSRF-TOKEN` header on same-origin requests. -> {tip} By default, the `resources/js/bootstrap.js` file includes the Axios HTTP library which will automatically send this for you. +> {tip} By default, the `resources/js/bootstrap.js` file includes the Axios HTTP library which will automatically send the `X-XSRF-TOKEN` header for you. diff --git a/sanctum.md b/sanctum.md index a8f96bd9a6..cdc0a68b26 100644 --- a/sanctum.md +++ b/sanctum.md @@ -224,19 +224,27 @@ Finally, you should ensure your application's session cookie domain configuratio ### Authenticating + +#### CSRF Protection + To authenticate your SPA, your SPA's login page should first make a request to the `/sanctum/csrf-cookie` route to initialize CSRF protection for the application: axios.get('/sanctum/csrf-cookie').then(response => { // Login... }); -During this request Laravel will set an `XSRF-TOKEN` cookie containing the current CSRF token. This token should then be passed in an `X-XSRF-TOKEN` header on subsequent requests, which libraries like Axios and the Angular HttpClient will do automatically for you. +During this request Laravel will set an `XSRF-TOKEN` cookie containing the current CSRF token. This token should then be passed in an `X-XSRF-TOKEN` header on subsequent requests, which some HTTP client libraries like Axios and the Angular HttpClient will do automatically for you. If your JavaScript HTTP library does not set the value for you, you will need to manually set the `X-XSRF-TOKEN` header to match the value of the `XSRF-TOKEN` cookie. + + +#### Logging In Once CSRF protection has been initialized, you should make a `POST` request to the your Laravel application's `/login` route. This `/login` route may be implemented manually or using one of Laravel's [application starter kits](/docs/{{version}}/starter-kits). -If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client. +If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client. In addition, since your application already made a request to the `/sanctum/csrf-cookie` route, subsequent requests should automatically receive CSRF protection as long as your JavaScript HTTP client sends the value of `XSRF-TOKEN` cookie in the `X-XSRF-TOKEN` header. + +Of course, if your users session expires due to lack of activity, subsequent requests to the Laravel backend may receive a 401 or 419 HTTP error response. In this case, you should redirect the user to your application's login page. -> {tip} You are free to write your own `/login` endpoint; however, you should ensure that it authenticates the user using the standard, [session based authentication services that Laravel provides](/docs/{{version}}/authentication#authenticating-users). +> {note} You are free to write your own `/login` endpoint; however, you should ensure that it authenticates the user using the standard, [session based authentication services that Laravel provides](/docs/{{version}}/authentication#authenticating-users). Typically, this means using the `web` authentication guard. ### Protecting Routes From 480ddb386e24298aacb058dd2d1bf46933bb2357 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 14:47:27 -0600 Subject: [PATCH 052/274] wip --- controllers.md | 125 +++++++++++++++++++++++++----------------------- csrf.md | 4 +- installation.md | 30 ++++++------ 3 files changed, 81 insertions(+), 78 deletions(-) diff --git a/controllers.md b/controllers.md index e7fffcd6a3..7c008baa64 100644 --- a/controllers.md +++ b/controllers.md @@ -1,8 +1,8 @@ # Controllers - [Introduction](#introduction) -- [Basic Controllers](#basic-controllers) - - [Defining Controllers](#defining-controllers) +- [Writing Controllers](#writing-controllers) + - [Basic Controllers](#basic-controllers) - [Single Action Controllers](#single-action-controllers) - [Controller Middleware](#controller-middleware) - [Resource Controllers](#resource-controllers) @@ -19,15 +19,15 @@ ## Introduction -Instead of defining all of your request handling logic as Closures in route files, you may wish to organize this behavior using Controller classes. Controllers can group related request handling logic into a single class. Controllers are stored in the `app/Http/Controllers` directory. +Instead of defining all of your request handling logic as Closures in your route files, you may wish to organize this behavior using "controller" classes. Controllers can group related request handling logic into a single class. For example, a `UserController` class might handle all incoming requests related to users, including showing, creating, updating, and deleting users. By default, controllers are stored in the `app/Http/Controllers` directory. - -## Basic Controllers + +## Writing Controllers - -### Defining Controllers + +### Basic Controllers -Below is an example of a basic controller class. Note that the controller extends the base controller class included with Laravel. The base class provides a few convenience methods such as the `middleware` method, which may be used to attach middleware to controller actions: +Below is an example of a basic controller class. Note that the controller extends the base controller class included with Laravel: `App\Http\Controllers\Controller`. The base class provides a few convenience methods such as the `middleware` method, which may be used to attach middleware to controller actions: User::findOrFail($id)]); + return view('user.profile', [ + 'user' => User::findOrFail($id) + ]); } } @@ -54,16 +56,16 @@ You can define a route to this controller action like so: use App\Http\Controllers\UserController; - Route::get('user/{id}', [UserController::class, 'show']); + Route::get('/user/{id}', [UserController::class, 'show']); -Now, when a request matches the specified route URI, the `show` method on the `UserController` class will be executed. The route parameters will also be passed to the method. +When an incoming request matches the specified route URI, the `show` method on the `UserController` class will be invoked and the route parameters will be passed to the method. -> {tip} Controllers are not **required** to extend a base class. However, you will not have access to convenience features such as the `middleware`, `validate`, and `dispatch` methods. +> {tip} Controllers are not **required** to extend a base class. However, you will not have access to convenient features such as the `middleware` and `authorize` methods. ### Single Action Controllers -If you would like to define a controller that only handles a single action, you may place a single `__invoke` method on the controller: +If a controller action is particularly complex, you might find it convenient to dedicate an entire controller class to that single action. To accomplish this, you may define a single `__invoke` method within the controller: User::findOrFail($id)]); + // ... } } -When registering routes for single action controllers, you do not need to specify a method: +When registering routes for single action controllers, you do not need to specify a controller method. Instead, you may simply pass the name of the controller to the router: - use App\Http\Controllers\ShowProfile; + use App\Http\Controllers\ProvisionServer; - Route::get('user/{id}', ShowProfile::class); + Route::post('/server', ProvisionServer::class); You may generate an invokable controller by using the `--invokable` option of the `make:controller` Artisan command: - php artisan make:controller ShowProfile --invokable + php artisan make:controller ProvisionServer --invokable > {tip} Controller stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization) @@ -105,7 +107,7 @@ You may generate an invokable controller by using the `--invokable` option of th Route::get('profile', [UserController::class, 'show'])->middleware('auth'); -However, it is more convenient to specify middleware within your controller's constructor. Using the `middleware` method from your controller's constructor, you may easily assign middleware to the controller's action. You may even restrict the middleware to only certain methods on the controller class: +Or, you may find it convenient to specify middleware within your controller's constructor. Using the `middleware` method from your controller's constructor, you can assign middleware to the controller's actions: class UserController extends Controller { @@ -117,39 +119,35 @@ However, it is more convenient to specify middleware within your controller's co public function __construct() { $this->middleware('auth'); - $this->middleware('log')->only('index'); - $this->middleware('subscribed')->except('store'); } } -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: +Controllers also allow you to register middleware using a Closure. This provides a convenient way to define an inline 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. - ## Resource Controllers -Laravel resource routing assigns the typical "CRUD" routes to a controller with a single line of code. For example, you may wish to create a controller that handles all HTTP requests for "photos" stored by your application. Using the `make:controller` Artisan command, we can quickly create such a controller: +If you think of each Eloquent model in your application as a "resource", it is typical to perform the same sets of actions against each resource in your application. For example, imagine your application contains a `Photo` model and a `Movie` model. It is likely that users can create, read, update, or delete these resources. + +Because of this common use case, Laravel resource routing assigns the typical create, read, update, and delete ("CRUD") routes to a controller with a single line of code. To get started, we can use the `make:controller` Artisan command's `--resource` option to quickly create a controller to handle these actions: php artisan make:controller PhotoController --resource -This command will generate a controller at `app/Http/Controllers/PhotoController.php`. The controller will contain a method for each of the available resource operations. +This command will generate a controller at `app/Http/Controllers/PhotoController.php`. The controller will contain a method for each of the available resource operations. Next, you may register a resource route that points to the controller: -Next, you may register a resourceful route to the controller: + use App\Http\Controllers\PhotoController; Route::resource('photos', PhotoController::class); -This single route declaration creates multiple routes to handle a variety of actions on the resource. The generated controller will already have methods stubbed for each of these actions, including notes informing you of the HTTP verbs and URIs they handle. +This single route declaration creates multiple routes to handle a variety of actions on the resource. The generated controller will already have methods stubbed for each of these actions. Remember, you can always get a quick overview of your application's by running the `route:list` Artisan command. -You may register many resource controllers at once by passing an array to the `resources` method: +You may even register many resource controllers at once by passing an array to the `resources` method: Route::resources([ 'photos' => PhotoController::class, @@ -172,7 +170,7 @@ 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: +If you are using [route model binding](/docs/{{version}}/routing#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 @@ -181,6 +179,8 @@ If you are using route model binding and would like the resource controller's me When declaring a resource route, you may specify a subset of actions the controller should handle instead of the full set of default actions: + use App\Http\Controllers\PhotoController; + Route::resource('photos', PhotoController::class)->only([ 'index', 'show' ]); @@ -194,10 +194,15 @@ When declaring a resource route, you may specify a subset of actions the control When declaring resource routes that will be consumed by APIs, you will commonly want to exclude routes that present HTML templates such as `create` and `edit`. For convenience, you may use the `apiResource` method to automatically exclude these two routes: + use App\Http\Controllers\PhotoController; + Route::apiResource('photos', PhotoController::class); You may register many API resource controllers at once by passing an array to the `apiResources` method: + use App\Http\Controllers\PhotoController; + use App\Http\Controllers\PostController; + Route::apiResources([ 'photos' => PhotoController::class, 'posts' => PostController::class, @@ -210,7 +215,9 @@ To quickly generate an API resource controller that does not include the `create ### Nested Resources -Sometimes you may need to define routes to a nested resource. For example, a photo resource may have multiple comments that may be attached to the photo. To nest the resource controllers, use "dot" notation in your route declaration: +Sometimes you may need to define routes to a nested resource. For example, a photo resource may have multiple comments that may be attached to the photo. To nest the resource controllers, you may use "dot" notation in your route declaration: + + use App\Http\Controllers\PhotoCommentController; Route::resource('photos.comments', PhotoCommentController::class); @@ -221,24 +228,18 @@ This route will register a nested resource that may be accessed with URIs like t #### Scoping Nested Resources -Laravel's [implicit model binding](/docs/{{version}}/routing#implicit-model-binding-scoping) feature can automatically scope nested bindings such that the resolved child model is confirmed to belong to the parent model. By using the `scoped` method when defining your nested resource, you may enable automatic scoping as well as instruct Laravel which field the child resource should be retrieved by: - - Route::resource('photos.comments', PhotoCommentController::class)->scoped([ - 'comment' => 'slug', - ]); - -This route will register a scoped nested resource that may be accessed with URIs like the following: - - /photos/{photo}/comments/{comment:slug} +Laravel's [implicit model binding](/docs/{{version}}/routing#implicit-model-binding-scoping) feature can automatically scope nested bindings such that the resolved child model is confirmed to belong to the parent model. By using the `scoped` method when defining your nested resource, you may enable automatic scoping as well as instruct Laravel which field the child resource should be retrieved by. For more information on how to accomplish this, please see the documentation on [scoping resource routes](#restful-scoping-resource-routes). #### Shallow Nesting Often, it is not entirely necessary to have both the parent and the child IDs within a URI since the child ID is already a unique identifier. When using unique identifiers such as auto-incrementing primary keys to identify your models in URI segments, you may choose to use "shallow nesting": + use App\Http\Controllers\CommentController; + Route::resource('photos.comments', CommentController::class)->shallow(); -The route definition above will define the following routes: +This route definition will define the following routes: Verb | URI | Action | Route Name ----------|-----------------------------------|--------------|--------------------- @@ -253,7 +254,9 @@ DELETE | `/comments/{comment}` | destroy | comments.destroy ### Naming Resource Routes -By default, all resource controller actions have a route name; however, you can override these names by passing a `names` array with your options: +By default, all resource controller actions have a route name; however, you can override these names by passing a `names` array with your desired route names: + + use App\Http\Controllers\PhotoController; Route::resource('photos', PhotoController::class)->names([ 'create' => 'photos.build' @@ -262,34 +265,34 @@ By default, all resource controller actions have a route name; however, you can ### Naming Resource Route Parameters -By default, `Route::resource` will create the route parameters for your resource routes based on the "singularized" version of the resource name. You can easily override this on a per resource basis by using the `parameters` method. The array passed into the `parameters` method should be an associative array of resource names and parameter names: +By default, `Route::resource` will create the route parameters for your resource routes based on the "singularized" version of the resource name. You can easily override this on a per resource basis using the `parameters` method. The array passed into the `parameters` method should be an associative array of resource names and parameter names: + + use App\Http\Controllers\AdminUserController; Route::resource('users', AdminUserController::class)->parameters([ 'users' => 'admin_user' ]); - The example above generates the following URIs for the resource's `show` route: + The example above generates the following URI for the resource's `show` route: /users/{admin_user} ### Scoping Resource Routes -Sometimes, when implicitly binding multiple Eloquent models in resource route definitions, you may wish to scope the second Eloquent model such that it must be a child of the first Eloquent model. For example, consider this situation that retrieves a blog post by slug for a specific user: +Laravel's [scoped implicit model binding](/docs/{{version}}/routing#implicit-model-binding-scoping) feature can automatically scope nested bindings such that the resolved child model is confirmed to belong to the parent model. By using the `scoped` method when defining your nested resource, you may enable automatic scoping as well as instruct Laravel which field the child resource should be retrieved by: - use App\Http\Controllers\PostsController; + use App\Http\Controllers\PhotoCommentController; - Route::resource('users.posts', PostsController::class)->scoped(); - -You may override the default model route keys by passing an array to the `scoped` method: + Route::resource('photos.comments', PhotoCommentController::class)->scoped([ + 'comment' => 'slug', + ]); - use App\Http\Controllers\PostsController; +This route will register a scoped nested resource that may be accessed with URIs like the following: - Route::resource('users.posts', PostsController::class)->scoped([ - 'post' => 'slug', - ]); + /photos/{photo}/comments/{comment:slug} -When using a custom keyed implicit binding as a nested route parameter, Laravel will automatically scope the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent. In this case, it will be assumed that the `User` model has a relationship named `posts` (the plural of the route parameter name) which can be used to retrieve the `Post` model. +When using a custom keyed implicit binding as a nested route parameter, Laravel will automatically scope the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent. In this case, it will be assumed that the `Photo` model has a relationship named `comments` (the plural of the route parameter name) which can be used to retrieve the `Comment` model. ### Localizing Resource URIs diff --git a/csrf.md b/csrf.md index 7f33796065..f55a46a0ee 100644 --- a/csrf.md +++ b/csrf.md @@ -2,7 +2,7 @@ - [Introduction](#csrf-introduction) - [Preventing CSRF Requests](#preventing-csrf-requests) -- [Excluding URIs](#csrf-excluding-uris) + - [Excluding URIs](#csrf-excluding-uris) - [X-CSRF-Token](#csrf-x-csrf-token) - [X-XSRF-Token](#csrf-x-xsrf-token) @@ -64,7 +64,7 @@ The `App\Http\Middleware\VerifyCsrfToken` [middleware](/docs/{{version}}/middlew If you are building an SPA that is utilizing Laravel as an API backend, you should consult the [Laravel Sanctum documentation](/docs/{{version}}/sanctum) for information on authenticating with your API and protecting against CSRF vulnerabilities. -## Excluding URIs From CSRF Protection +### Excluding URIs From CSRF Protection Sometimes you may wish to exclude a set of URIs from CSRF protection. For example, if you are using [Stripe](https://stripe.com) to process payments and are utilizing their webhook system, you will need to exclude your Stripe webhook handler route from CSRF protection since Stripe will not know what CSRF token to send to your routes. diff --git a/installation.md b/installation.md index 2dc5e0d1a4..71f585eebc 100644 --- a/installation.md +++ b/installation.md @@ -64,18 +64,18 @@ Laravel Sail is a light-weight command-line interface for interacting with Larav ### Getting Started On macOS -If you're developing on a Mac and [Docker Desktop](https://www.docker.com/products/docker-desktop) is already installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: +If you're developing on a Mac and [Docker Desktop](https://www.docker.com/products/docker-desktop) is already installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: ```bash -curl -s https://laravel.build/my-app | bash +curl -s https://laravel.build/example-app | bash ``` -Of course, you can change `my-app` in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. +Of course, you can change `example-app` in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: ```bash -cd my-app +cd example-app ./sail up ``` @@ -89,18 +89,18 @@ Before we create a new Laravel application on your Windows machine, make sure to > {tip} After installing and enabling WSL2, you should ensure that Docker Desktop is [configured to use the WSL2 backend](https://docs.docker.com/docker-for-windows/wsl/). -Next, you are ready to create your first Laravel project. Launch [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) and begin a new terminal session for your WSL2 Linux operating system. Next, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: +Next, you are ready to create your first Laravel project. Launch [Windows Terminal](https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab) and begin a new terminal session for your WSL2 Linux operating system. Next, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: ```bash -curl -s https://laravel.build/my-app | bash +curl -s https://laravel.build/example-app | bash ``` -Of course, you can change `my-app` in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. +Of course, you can change `example-app` in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: ```bash -cd my-app +cd example-app ./sail up ``` @@ -116,18 +116,18 @@ Once these tools are installed, you may open any Laravel project by executing th ### Getting Started On Linux -If you're developing on Linux and [Docker](https://www.docker.com) is already installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "my-app", you may run the following command in your terminal: +If you're developing on Linux and [Docker](https://www.docker.com) is already installed, you can use a simple terminal command to create a new Laravel project. For example, to create a new Laravel application in a directory named "example-app", you may run the following command in your terminal: ```bash -curl -s https://laravel.build/my-app | bash +curl -s https://laravel.build/example-app | bash ``` -Of course, you can change `my-app` in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. +Of course, you can change `example-app` in this URL to anything you like. The Laravel application's directory will be created within the directory you execute the command from. After the project has been created, you can navigate to the application directory and start Laravel Sail. Laravel Sail provides a simple command-line interface for interacting with Laravel's default Docker configuration: ```bash -cd my-app +cd example-app ./sail up ``` @@ -139,9 +139,9 @@ Once the application's Docker containers have been started, you can access the a If your computer already has PHP and Composer installed, you may create a new Laravel project by using Composer directly. After the application has been created, you may start Laravel's local development server using the Artisan CLI's `serve` command: - composer create-project laravel/laravel my-app + composer create-project laravel/laravel example-app - cd my-app + cd example-app php artisan serve @@ -152,7 +152,7 @@ Or, you may install the Laravel Installer as a global Composer dependency: composer global require laravel/installer - laravel new my-app + laravel new example-app php artisan serve From 174b68831aa34e77c990234c3def72bf4e7cc521 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 14:50:24 -0600 Subject: [PATCH 053/274] wip --- controllers.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/controllers.md b/controllers.md index 7c008baa64..9db2c8d24b 100644 --- a/controllers.md +++ b/controllers.md @@ -297,12 +297,10 @@ When using a custom keyed implicit binding as a nested route parameter, Laravel ### Localizing Resource URIs -By default, `Route::resource` will create resource URIs using English verbs. If you need to localize the `create` and `edit` action verbs, you may use the `Route::resourceVerbs` method. This may be done in the `boot` method of your `AppServiceProvider`: - - use Illuminate\Support\Facades\Route; +By default, `Route::resource` will create resource URIs using English verbs. If you need to localize the `create` and `edit` action verbs, you may use the `Route::resourceVerbs` method. This may be done at the beginning of the `boot` method within your application's `App\Providers\RouteServiceProvider`: /** - * Bootstrap any application services. + * Define your route model bindings, pattern filters, etc. * * @return void */ @@ -312,9 +310,11 @@ By default, `Route::resource` will create resource URIs using English verbs. If 'create' => 'crear', 'edit' => 'editar', ]); + + // ... } -Once the verbs have been customized, a resource route registration such as `Route::resource('fotos', 'PhotoController')` will produce the following URIs: +Once the verbs have been customized, a resource route registration such as `Route::resource('fotos', PhotoController::class)` will produce the following URIs: /fotos/crear @@ -323,10 +323,11 @@ Once the verbs have been customized, a resource route registration such as `Rout ### Supplementing Resource Controllers -If you need to add additional routes to a resource controller beyond the default set of resource routes, you should define those routes before your call to `Route::resource`; otherwise, the routes defined by the `resource` method may unintentionally take precedence over your supplemental routes: +If you need to add additional routes to a resource controller beyond the default set of resource routes, you should define those routes before your call to the `Route::resource` method; otherwise, the routes defined by the `resource` method may unintentionally take precedence over your supplemental routes: - Route::get('photos/popular', [PhotoController::class, 'popular']); + use App\Http\Controller\PhotoController; + Route::get('/photos/popular', [PhotoController::class, 'popular']); Route::resource('photos', PhotoController::class); > {tip} Remember to keep your controllers focused. If you find yourself routinely needing methods outside of the typical set of resource actions, consider splitting your controller into two, smaller controllers. From 85ec2948f9a0e716f4d45cc936d4b000249548bc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 15:00:05 -0600 Subject: [PATCH 054/274] wip" --- controllers.md | 4 +--- routing.md | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/controllers.md b/controllers.md index 9db2c8d24b..9972d1384b 100644 --- a/controllers.md +++ b/controllers.md @@ -356,7 +356,7 @@ The Laravel [service container](/docs/{{version}}/container) is used to resolve /** * Create a new controller instance. * - * @param UserRepository $users + * @param \App\Repositories\UserRepository $users * @return void */ public function __construct(UserRepository $users) @@ -365,8 +365,6 @@ The Laravel [service container](/docs/{{version}}/container) is used to resolve } } -You may also type-hint any [Laravel contract](/docs/{{version}}/contracts). If the container can resolve it, you can type-hint it. Depending on your application, injecting your dependencies into your controller may provide better testability. - #### Method Injection diff --git a/routing.md b/routing.md index 0b9bd54208..ccc36c58c0 100644 --- a/routing.md +++ b/routing.md @@ -70,6 +70,17 @@ Sometimes you may need to register a route that responds to multiple HTTP verbs. // }); + +#### Dependency Injection + +You may type-hint any dependencies required by your route in your route's callback signature. The declared dependencies will automatically be resolved and injected into the callback by the Laravel [service container](/docs/{{version}}/container). For example, you may type-hint the `Illuminate\Http\Request` class to have the current HTTP request automatically injected into your route callback: + + use Illuminate\Http\Request; + + Route::get('/users', function (Request $request) { + // ... + }); + #### CSRF Protection @@ -116,28 +127,39 @@ If your route only needs to return a [view](/docs/{{version}}/views), you may us Sometimes you will need to capture segments of the URI within your route. For example, you may need to capture a user's ID from the URL. You may do so by defining route parameters: - Route::get('user/{id}', function ($id) { + Route::get('/user/{id}', function ($id) { return 'User '.$id; }); You may define as many route parameters as required by your route: - Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) { + Route::get('/posts/{post}/comments/{comment}', function ($postId, $commentId) { // }); Route parameters are always encased within `{}` braces and should consist of alphabetic characters. Underscores (`_`) are also acceptable within route parameter names. Route parameters are injected into route callbacks / controllers based on their order - the names of the route callback / controller arguments do not matter. + +#### Parameters & Dependency Injection + +If your route has dependencies that you would like the Laravel service container to automatically inject into your route's callback, you should list your route parameters after your dependencies: + + use Illuminate\Http\Request; + + Route::get('/user/{id}', function (Request $request, $id) { + return 'User '.$id; + }); + ### Optional Parameters Occasionally you may need to specify a route parameter that may not always be present in the URI. You may do so by placing a `?` mark after the parameter name. Make sure to give the route's corresponding variable a default value: - Route::get('user/{name?}', function ($name = null) { + Route::get('/user/{name?}', function ($name = null) { return $name; }); - Route::get('user/{name?}', function ($name = 'John') { + Route::get('/user/{name?}', function ($name = 'John') { return $name; }); @@ -146,25 +168,25 @@ Occasionally you may need to specify a route parameter that may not always be pr 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) { + Route::get('/user/{name}', function ($name) { // })->where('name', '[A-Za-z]+'); - Route::get('user/{id}', function ($id) { + Route::get('/user/{id}', function ($id) { // })->where('id', '[0-9]+'); - Route::get('user/{id}/{name}', function ($id, $name) { + Route::get('/user/{id}/{name}', function ($id, $name) { // })->where(['id' => '[0-9]+', 'name' => '[a-z]+']); For convenience, some commonly used regular expression patterns have helper methods that allow you to quickly add pattern constraints to your routes: - Route::get('user/{id}/{name}', function ($id, $name) { + Route::get('/user/{id}/{name}', function ($id, $name) { // })->whereNumeric('id')->whereAlpha('name'); - Route::get('user/{id}', function ($id) { + Route::get('/user/{id}', function ($id) { // })->whereUuid('id'); From 249dc5a44a2b02856cecc8a38961dadf3585ede8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 15:02:49 -0600 Subject: [PATCH 055/274] wip --- controllers.md | 22 +++++----------------- routing.md | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/controllers.md b/controllers.md index 9972d1384b..ca2c98a5f2 100644 --- a/controllers.md +++ b/controllers.md @@ -14,7 +14,6 @@ - [Localizing Resource URIs](#restful-localizing-resource-uris) - [Supplementing Resource Controllers](#restful-supplementing-resource-controllers) - [Dependency Injection & Controllers](#dependency-injection-and-controllers) -- [Route Caching](#route-caching) ## Introduction @@ -394,7 +393,9 @@ In addition to constructor injection, you may also type-hint dependencies on you If your controller method is also expecting input from a route parameter, list your route arguments after your other dependencies. For example, if your route is defined like so: - Route::put('user/{id}', [UserController::class, 'update']); + use App\Http\Controllers\UserController; + + Route::put('/user/{id}', [UserController::class, 'update']); You may still type-hint the `Illuminate\Http\Request` and access your `id` parameter by defining your controller method as follows: @@ -409,25 +410,12 @@ You may still type-hint the `Illuminate\Http\Request` and access your `id` param /** * Update the given user. * - * @param Request $request + * @param \Illuminate\Http\Request $request * @param string $id - * @return Response + * @return \Illuminate\Http\Response */ public function update(Request $request, $id) { // } } - - -## Route Caching - -If your application is exclusively using controller based routes, you should take advantage of Laravel's route cache. Using the route cache will drastically decrease the amount of time it takes to register all of your application's routes. In some cases, your route registration may even be up to 100x faster. To generate a route cache, just execute the `route:cache` Artisan command: - - php artisan route:cache - -After running this command, your cached routes file will be loaded on every request. Remember, if you add any new routes you will need to generate a fresh route cache. Because of this, you should only run the `route:cache` command during your project's deployment. - -You may use the `route:clear` command to clear the route cache: - - php artisan route:clear diff --git a/routing.md b/routing.md index ccc36c58c0..a3b33ab361 100644 --- a/routing.md +++ b/routing.md @@ -23,6 +23,7 @@ - [Form Method Spoofing](#form-method-spoofing) - [Accessing The Current Route](#accessing-the-current-route) - [Cross-Origin Resource Sharing (CORS)](#cors) +- [Route Caching](#route-caching) ## Basic Routing @@ -637,3 +638,16 @@ You may refer to the API documentation for both the [underlying class of the Rou Laravel can automatically respond to CORS `OPTIONS` HTTP requests with values that you configure. All CORS settings may be configured in your application's `config/cors.php` configuration file. The `OPTIONS` requests will automatically be handled by the `HandleCors` [middleware](/docs/{{version}}/middleware) that is included by default in your global middleware stack. Your global middleware stack is located in your application's HTTP kernel (`App\Http\Kernel`). > {tip} For more information on CORS and CORS headers, please consult the [MDN web documentation on CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#The_HTTP_response_headers). + + +## Route Caching + +When deploying your application to production, you should take advantage of Laravel's route cache. Using the route cache will drastically decrease the amount of time it takes to register all of your application's routes. To generate a route cache, execute the `route:cache` Artisan command: + + php artisan route:cache + +After running this command, your cached routes file will be loaded on every request. Remember, if you add any new routes you will need to generate a fresh route cache. Because of this, you should only run the `route:cache` command during your project's deployment. + +You may use the `route:clear` command to clear the route cache: + + php artisan route:clear From eeaac1ad16c7b8f19d1bf74ff9929707bd46c0dd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 15:20:38 -0600 Subject: [PATCH 056/274] update usage of closure --- artisan.md | 14 +++++++------- authentication.md | 4 ++-- authorization.md | 8 ++++---- blade.md | 8 ++++---- broadcasting.md | 4 ++-- cache.md | 8 ++++---- container.md | 6 +++--- contracts.md | 2 +- controllers.md | 4 ++-- database-testing.md | 8 ++++---- database.md | 2 +- dusk.md | 4 ++-- eloquent-relationships.md | 6 +++--- eloquent-resources.md | 4 ++-- eloquent.md | 16 ++++++++-------- errors.md | 4 ++-- events.md | 6 +++--- filesystem.md | 2 +- helpers.md | 30 +++++++++++++++--------------- http-client.md | 4 ++-- mail.md | 2 +- migrations.md | 4 ++-- notifications.md | 2 +- packages.md | 2 +- passwords.md | 4 ++-- queries.md | 20 ++++++++++---------- queues.md | 12 ++++++------ redis.md | 4 ++-- releases.md | 10 +++++----- requests.md | 16 ++++++++-------- responses.md | 2 +- routing.md | 6 +++--- scheduling.md | 12 ++++++------ scout.md | 2 +- session.md | 2 +- structure.md | 2 +- validation.md | 12 ++++++------ views.md | 2 +- 38 files changed, 130 insertions(+), 130 deletions(-) diff --git a/artisan.md b/artisan.md index 14fe0f4d9d..a92e0ea573 100644 --- a/artisan.md +++ b/artisan.md @@ -147,10 +147,10 @@ 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: +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. + * Register the closure based commands for the application. * * @return void */ @@ -159,18 +159,18 @@ Closure based commands provide an alternative to defining console commands as cl 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: +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. +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): +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\Models\User; use App\Support\DripEmailer; @@ -182,7 +182,7 @@ In addition to receiving your command's arguments and options, command Closures #### 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: +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}!"); @@ -356,7 +356,7 @@ The `anticipate` method can be used to provide auto-completion for possible choi $name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']); -Alternatively, you may pass a Closure as the second argument to the `anticipate` method. The Closure will be called each time the user types an input character. The Closure should accept a string parameter containing the user's input so far, and return an array of options for auto-completion: +Alternatively, you may pass a closure as the second argument to the `anticipate` method. The closure will be called each time the user types an input character. The closure should accept a string parameter containing the user's input so far, and return an array of options for auto-completion: $name = $this->anticipate('What is your name?', function ($input) { // Return auto-completion options... diff --git a/authentication.md b/authentication.md index 2039e99ea7..a9a23bc271 100644 --- a/authentication.md +++ b/authentication.md @@ -525,9 +525,9 @@ As you can see in the example above, the callback passed to the `extend` method ### Closure Request Guards -The simplest way to implement a custom, HTTP request based authentication system is by using the `Auth::viaRequest` method. This method allows you to quickly define your authentication process using a single Closure. +The simplest way to implement a custom, HTTP request based authentication system is by using the `Auth::viaRequest` method. This method allows you to quickly define your authentication process using a single closure. -To get started, call the `Auth::viaRequest` method within the `boot` method of your `AuthServiceProvider`. The `viaRequest` method accepts an authentication driver name as its first argument. This name can be any string that describes your custom guard. The second argument passed to the method should be a Closure that receives the incoming HTTP request and returns a user instance or, if authentication fails, `null`: +To get started, call the `Auth::viaRequest` method within the `boot` method of your `AuthServiceProvider`. The `viaRequest` method accepts an authentication driver name as its first argument. This name can be any string that describes your custom guard. The second argument passed to the method should be a closure that receives the incoming HTTP request and returns a user instance or, if authentication fails, `null`: use App\Models\User; use Illuminate\Http\Request; diff --git a/authorization.md b/authorization.md index c326a7cc84..be3c9833c9 100644 --- a/authorization.md +++ b/authorization.md @@ -27,7 +27,7 @@ In addition to providing [authentication](/docs/{{version}}/authentication) services out of the box, Laravel also provides a simple way to authorize user actions against a given resource. Like authentication, Laravel's approach to authorization is simple, and there are two primary ways of authorizing actions: gates and policies. -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. +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. 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. @@ -37,7 +37,7 @@ You do not need to choose between exclusively using gates or exclusively using p ### 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: +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: /** * Register any authentication / authorization services. @@ -76,7 +76,7 @@ Gates may also be defined using a class callback array, like controllers: ### Authorizing Actions -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: +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('edit-settings')) { // The current user can edit settings @@ -402,7 +402,7 @@ The `User` model that is included with your Laravel application includes two hel // } -If a [policy is registered](#registering-policies) for the given model, the `can` method will automatically call the appropriate policy and return the boolean result. If no policy is registered for the model, the `can` method will attempt to call the Closure based Gate matching the given action name. +If a [policy is registered](#registering-policies) for the given model, the `can` method will automatically call the appropriate policy and return the boolean result. If no policy is registered for the model, the `can` method will attempt to call the closure based Gate matching the given action name. #### Actions That Don't Require Models diff --git a/blade.md b/blade.md index fd1e5b9f7a..4cd46788ff 100644 --- a/blade.md +++ b/blade.md @@ -663,7 +663,7 @@ You may execute this method from your component template by invoking the variabl #### Using Attributes & Slots Inside The Class -Blade components also allow you to access the component name, attributes, and slot inside the class's render method. However, in order to access this data, you should return a Closure from your component's `render` method. The Closure will receive a `$data` array as its only argument: +Blade components also allow you to access the component name, attributes, and slot inside the class's render method. However, in order to access this data, you should return a closure from your component's `render` method. The closure will receive a `$data` array as its only argument: /** * Get the view / contents that represent the component. @@ -683,7 +683,7 @@ Blade components also allow you to access the component name, attributes, and sl The `componentName` is equal to the name used in the HTML tag after the `x-` prefix. So ``'s `componentName` will be `alert`. The `attributes` element will contain all of the attributes that were present on the HTML tag. The `slot` element is an `Illuminate\Support\HtmlString` instance with the contents of the slot from the component. -The Closure should return a string. If the returned string corresponds to an existing view, that view will be rendered; otherwise, the returned string will be evaluated as an inline Blade view. +The closure should return a string. If the returned string corresponds to an existing view, that view will be rendered; otherwise, the returned string will be evaluated as an inline Blade view. #### Additional Dependencies @@ -771,7 +771,7 @@ If you would like an attribute other than `class` to have its values appended to #### Filtering Attributes -You may filter attributes using the `filter` method. This method accepts a Closure which should return `true` if you wish to retain the attribute in the attribute bag: +You may filter attributes using the `filter` method. This method accepts a closure which should return `true` if you wish to retain the attribute in the attribute bag: {{ $attributes->filter(fn ($value, $key) => $key == 'foo') }} @@ -1047,7 +1047,7 @@ As you can see, we will chain the `format` method onto whatever expression is pa ### Custom If Statements -Programming a custom directive is sometimes more complex than necessary when defining simple, custom conditional statements. For that reason, Blade provides a `Blade::if` method which allows you to quickly define custom conditional directives using Closures. For example, let's define a custom conditional that checks the current application cloud provider. We may do this in the `boot` method of our `AppServiceProvider`: +Programming a custom directive is sometimes more complex than necessary when defining simple, custom conditional statements. For that reason, Blade provides a `Blade::if` method which allows you to quickly define custom conditional directives using closures. For example, let's define a custom conditional that checks the current application cloud provider. We may do this in the `boot` method of our `AppServiceProvider`: use Illuminate\Support\Facades\Blade; diff --git a/broadcasting.md b/broadcasting.md index a79e70f54a..4bf3bf2879 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -404,7 +404,7 @@ Private and presence broadcast channels authenticate the current user via your a ### Defining Channel Classes -If your application is consuming many different channels, your `routes/channels.php` file could become bulky. So, instead of using Closures to authorize channels, you may use channel classes. To generate a channel class, use the `make:channel` Artisan command. This command will place a new channel class in the `App/Broadcasting` directory. +If your application is consuming many different channels, your `routes/channels.php` file could become bulky. So, instead of using closures to authorize channels, you may use channel classes. To generate a channel class, use the `make:channel` Artisan command. This command will place a new channel class in the `App/Broadcasting` directory. php artisan make:channel OrderChannel @@ -414,7 +414,7 @@ Next, register your channel in your `routes/channels.php` file: Broadcast::channel('order.{order}', OrderChannel::class); -Finally, you may place the authorization logic for your channel in the channel class' `join` method. This `join` method will house the same logic you would have typically placed in your channel authorization Closure. You may also take advantage of channel model binding: +Finally, you may place the authorization logic for your channel in the channel class' `join` method. This `join` method will house the same logic you would have typically placed in your channel authorization closure. You may also take advantage of channel model binding: get(); @@ -157,7 +157,7 @@ Sometimes you may wish to retrieve an item from the cache, but also store a defa return DB::table('users')->get(); }); -If the item does not exist in the cache, the `Closure` passed to the `remember` method will be executed and its result will be placed in the cache. +If the item does not exist in the cache, the closure passed to the `remember` method will be executed and its result will be placed in the cache. You may use the `rememberForever` method to retrieve an item from the cache or store it forever: @@ -311,7 +311,7 @@ Atomic locks allow for the manipulation of distributed locks without worrying ab $lock->release(); } -The `get` method also accepts a Closure. After the Closure is executed, Laravel will automatically release the lock: +The `get` method also accepts a closure. After the closure is executed, Laravel will automatically release the lock: Cache::lock('foo')->get(function () { // Lock acquired indefinitely and automatically released... @@ -432,7 +432,7 @@ To register the custom cache driver with Laravel, we will use the `extend` metho } } -The first argument passed to the `extend` method is the name of the driver. This will correspond to your `driver` option in the `config/cache.php` configuration file. The second argument is a Closure that should return an `Illuminate\Cache\Repository` instance. The Closure will be passed an `$app` instance, which is an instance of the [service container](/docs/{{version}}/container). +The first argument passed to the `extend` method is the name of the driver. This will correspond to your `driver` option in the `config/cache.php` configuration file. The second argument is a closure that should return an `Illuminate\Cache\Repository` instance. The closure will be passed an `$app` instance, which is an instance of the [service container](/docs/{{version}}/container). Once your extension is registered, update your `config/cache.php` configuration file's `driver` option to the name of your extension. diff --git a/container.md b/container.md index 3c74fbbe1d..01691c5820 100644 --- a/container.md +++ b/container.md @@ -116,7 +116,7 @@ First, if you write a class that implements an interface and you wish to type-hi Almost all of your service container bindings will be registered within [service providers](/docs/{{version}}/providers), so most of these examples will demonstrate using the container in that context. -Within a service provider, you always have access to the container via the `$this->app` property. We can register a binding using the `bind` method, passing the class or interface name that we wish to register along with a `Closure` that returns an instance of the class: +Within a service provider, you always have access to the container via the `$this->app` property. We can register a binding using the `bind` method, passing the class or interface name that we wish to register along with a closure that returns an instance of the class: use App\Services\Transistor; use App\Services\PodcastParser; @@ -265,7 +265,7 @@ Occasionally you may have a class that receives an array of typed objects using } } -Using contextual binding, you may resolve this dependency by providing the `give` method with a Closure that returns an array of resolved `Filter` instances: +Using contextual binding, you may resolve this dependency by providing the `give` method with a closure that returns an array of resolved `Filter` instances: $this->app->when(Firewall::class) ->needs(Filter::class) @@ -320,7 +320,7 @@ Once the services have been tagged, you may easily resolve them all via the cont ### Extending Bindings -The `extend` method allows the modification of resolved services. For example, when a service is resolved, you may run additional code to decorate or configure the service. The `extend` method accepts a Closure, which should return the modified service, as its only argument. The Closure receives the service being resolved and the container instance: +The `extend` method allows the modification of resolved services. For example, when a service is resolved, you may run additional code to decorate or configure the service. The `extend` method accepts a closure, which should return the modified service, as its only argument. The closure receives the service being resolved and the container instance: $this->app->extend(Service::class, function ($service, $app) { return new DecoratedService($service); diff --git a/contracts.md b/contracts.md index 0bf01289b2..5c27ba2712 100644 --- a/contracts.md +++ b/contracts.md @@ -34,7 +34,7 @@ In general, most applications can use facades without issue during development. So, how do you get an implementation of a contract? It's actually quite simple. -Many types of classes in Laravel are resolved through the [service container](/docs/{{version}}/container), including controllers, event listeners, middleware, queued jobs, and even route Closures. So, to get an implementation of a contract, you can just "type-hint" the interface in the constructor of the class being resolved. +Many types of classes in Laravel are resolved through the [service container](/docs/{{version}}/container), including controllers, event listeners, middleware, queued jobs, and even route closures. So, to get an implementation of a contract, you can just "type-hint" the interface in the constructor of the class being resolved. For example, take a look at this event listener: diff --git a/controllers.md b/controllers.md index ca2c98a5f2..653f29ea92 100644 --- a/controllers.md +++ b/controllers.md @@ -18,7 +18,7 @@ ## Introduction -Instead of defining all of your request handling logic as Closures in your route files, you may wish to organize this behavior using "controller" classes. Controllers can group related request handling logic into a single class. For example, a `UserController` class might handle all incoming requests related to users, including showing, creating, updating, and deleting users. By default, controllers are stored in the `app/Http/Controllers` directory. +Instead of defining all of your request handling logic as closures in your route files, you may wish to organize this behavior using "controller" classes. Controllers can group related request handling logic into a single class. For example, a `UserController` class might handle all incoming requests related to users, including showing, creating, updating, and deleting users. By default, controllers are stored in the `app/Http/Controllers` directory. ## Writing Controllers @@ -123,7 +123,7 @@ Or, you may find it 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 an inline middleware for a single controller without defining an entire middleware class: +Controllers also allow you to register middleware using a closure. This provides a convenient way to define an inline middleware for a single controller without defining an entire middleware class: $this->middleware(function ($request, $next) { return $next($request); diff --git a/database-testing.md b/database-testing.md index 82253f2acf..91185c14b8 100644 --- a/database-testing.md +++ b/database-testing.md @@ -354,7 +354,7 @@ By convention, when passing a `Post` model to the `has` method, Laravel will ass ->has(Post::factory()->count(3), 'posts') ->create(); -Of course, you may perform state manipulations on the related models. In addition, you may pass a Closure based state transformation if your state change requires access to the parent model: +Of course, you may perform state manipulations on the related models. In addition, you may pass a closure based state transformation if your state change requires access to the parent model: $user = User::factory() ->has( @@ -383,7 +383,7 @@ When using magic methods to create factory relationships, you may pass an array ]) ->create(); -You may provide a Closure based state transformation if your state change requires access to the parent model: +You may provide a closure based state transformation if your state change requires access to the parent model: $user = User::factory() ->hasPosts(3, function (array $attributes, User $user) { @@ -445,7 +445,7 @@ If you need to define attributes that should be set on the pivot / intermediate ) ->create(); -You may provide a Closure based state transformation if your state change requires access to the related model: +You may provide a closure based state transformation if your state change requires access to the related model: $users = User::factory() ->hasAttached( @@ -560,7 +560,7 @@ Alternatively, you may instruct the `RefreshDatabase` trait to automatically see * @var bool */ protected $seed = true; - + // ... } diff --git a/database.md b/database.md index d692b7e0a8..18b0dda7a5 100644 --- a/database.md +++ b/database.md @@ -220,7 +220,7 @@ If you would like to receive each SQL query executed by your application, you ma ## Database Transactions -You may use the `transaction` method on the `DB` facade to run a set of operations within a database transaction. If an exception is thrown within the transaction `Closure`, the transaction will automatically be rolled back. If the `Closure` executes successfully, the transaction will automatically be committed. You don't need to worry about manually rolling back or committing while using the `transaction` method: +You may use the `transaction` method on the `DB` facade to run a set of operations within a database transaction. If an exception is thrown within the transaction closure, the transaction will automatically be rolled back. If the closure executes successfully, the transaction will automatically be committed. You don't need to worry about manually rolling back or committing while using the `transaction` method: DB::transaction(function () { DB::table('users')->update(['votes' => 1]); diff --git a/dusk.md b/dusk.md index cbfc260983..5502d4bd80 100644 --- a/dusk.md +++ b/dusk.md @@ -301,7 +301,7 @@ If you would like to define a custom browser method that you can re-use in a var } } -The `macro` function accepts a name as its first argument, and a Closure as its second. The macro's Closure will be executed when calling the macro as a method on a `Browser` implementation: +The `macro` function accepts a name as its first argument, and a closure as its second. The macro's closure will be executed when calling the macro as a method on a `Browser` implementation: $this->browse(function ($browser) use ($user) { $browser->visit('/pay') @@ -764,7 +764,7 @@ The following methods may be used to wait until a given Vue component attribute #### Waiting With A Callback -Many of the "wait" methods in Dusk rely on the underlying `waitUsing` method. You may use this method directly to wait for a given callback to return `true`. The `waitUsing` method accepts the maximum number of seconds to wait, the interval at which the Closure should be evaluated, the Closure, and an optional failure message: +Many of the "wait" methods in Dusk rely on the underlying `waitUsing` method. You may use this method directly to wait for a given callback to return `true`. The `waitUsing` method accepts the maximum number of seconds to wait, the interval at which the closure should be evaluated, the closure, and an optional failure message: $browser->waitUsing(10, 1, function () use ($something) { return $something->isReady(); diff --git a/eloquent-relationships.md b/eloquent-relationships.md index dfe691af73..576785b3c1 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -1109,7 +1109,7 @@ In addition, using the `loadCount` method, you may load a relationship count aft $book->loadCount('genres'); -If you need to set additional query constraints on the eager loading query, you may pass an array keyed by the relationships you wish to load. The array values should be `Closure` instances which receive the query builder instance: +If you need to set additional query constraints on the eager loading query, you may pass an array keyed by the relationships you wish to load. The array values should be closure instances which receive the query builder instance: $book->loadCount(['reviews' => function ($query) { $query->where('rating', 5); @@ -1324,7 +1324,7 @@ Sometimes you may need to eager load a relationship after the parent model has a $books->load('author', 'publisher'); } -If you need to set additional query constraints on the eager loading query, you may pass an array keyed by the relationships you wish to load. The array values should be `Closure` instances which receive the query instance: +If you need to set additional query constraints on the eager loading query, you may pass an array keyed by the relationships you wish to load. The array values should be closure instances which receive the query instance: $author->load(['books' => function ($query) { $query->orderBy('published_date', 'asc'); @@ -1480,7 +1480,7 @@ The `belongsTo`, `hasOne`, `hasOneThrough`, and `morphOne` relationships allow y return $this->belongsTo('App\Models\User')->withDefault(); } -To populate the default model with attributes, you may pass an array or Closure to the `withDefault` method: +To populate the default model with attributes, you may pass an array or closure to the `withDefault` method: /** * Get the author of the post. diff --git a/eloquent-resources.md b/eloquent-resources.md index 2f2bdc83e8..b5402eccc4 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -500,7 +500,7 @@ Sometimes you may wish to only include an attribute in a resource response if a In this example, the `secret` key will only be returned in the final resource response if the authenticated user's `isAdmin` method returns `true`. If the method returns `false`, the `secret` key will be removed from the resource response entirely before it is sent back to the client. The `when` method allows you to expressively define your resources without resorting to conditional statements when building the array. -The `when` method also accepts a Closure as its second argument, allowing you to calculate the resulting value only if the given condition is `true`: +The `when` method also accepts a closure as its second argument, allowing you to calculate the resulting value only if the given condition is `true`: 'secret' => $this->when(Auth::user()->isAdmin(), function () { return 'secret-value'; @@ -566,7 +566,7 @@ In this example, if the relationship has not been loaded, the `posts` key will b #### Conditional Pivot Information -In addition to conditionally including relationship information in your resource responses, you may conditionally include data from the intermediate tables of many-to-many relationships using the `whenPivotLoaded` method. The `whenPivotLoaded` method accepts the name of the pivot table as its first argument. The second argument should be a Closure that defines the value to be returned if the pivot information is available on the model: +In addition to conditionally including relationship information in your resource responses, you may conditionally include data from the intermediate tables of many-to-many relationships using the `whenPivotLoaded` method. The `whenPivotLoaded` method accepts the name of the pivot table as its first argument. The second argument should be a closure that defines the value to be returned if the pivot information is available on the model: /** * Transform the resource into an array. diff --git a/eloquent.md b/eloquent.md index ba60a7a53b..ff313bbe48 100644 --- a/eloquent.md +++ b/eloquent.md @@ -306,7 +306,7 @@ You may also loop over the collection like an array: ### Chunking Results -If you need to process thousands of Eloquent records, use the `chunk` command. The `chunk` method will retrieve a "chunk" of Eloquent models, feeding them to a given `Closure` for processing. Using the `chunk` method will conserve memory when working with large result sets: +If you need to process thousands of Eloquent records, use the `chunk` command. The `chunk` method will retrieve a "chunk" of Eloquent models, feeding them to a given closure for processing. Using the `chunk` method will conserve memory when working with large result sets: Flight::chunk(200, function ($flights) { foreach ($flights as $flight) { @@ -314,7 +314,7 @@ If you need to process thousands of Eloquent records, use the `chunk` command. T } }); -The first argument passed to the method is the number of records you wish to receive per "chunk". The Closure passed as the second argument will be called for each chunk that is retrieved from the database. A database query will be executed to retrieve each chunk of records passed to the Closure. +The first argument passed to the method is the number of records you wish to receive per "chunk". The closure passed as the second argument will be called for each chunk that is retrieved from the database. A database query will be executed to retrieve each chunk of records passed to the closure. If you are filtering the results of the `chunk` method based on a column that you will also be updating while iterating over the results, you should use the `chunkById` method. Using the `chunk` method in these scenarios could lead to unexpected and inconsistent results: @@ -871,7 +871,7 @@ After adding the scope, a query to `User::all()` will produce the following SQL: #### Anonymous Global Scopes -Eloquent also allows you to define global scopes using Closures, which is particularly useful for simple scopes that do not warrant a separate class: +Eloquent also allows you to define global scopes using closures, which is particularly useful for simple scopes that do not warrant a separate class: get(); -Or, if you defined the global scope using a Closure: +Or, if you defined the global scope using a closure: User::withoutGlobalScope('age')->get(); @@ -961,13 +961,13 @@ Once the scope has been defined, you may call the scope methods when querying th $users = App\Models\User::popular()->active()->orderBy('created_at')->get(); -Combining multiple Eloquent model scopes via an `or` query operator may require the use of Closure callbacks: +Combining multiple Eloquent model scopes via an `or` query operator may require the use of closure callbacks: $users = App\Models\User::popular()->orWhere(function (Builder $query) { $query->active(); })->get(); -However, since this can be cumbersome, Laravel provides a "higher order" `orWhere` method that allows you to fluently chain these scopes together without the use of Closures: +However, since this can be cumbersome, Laravel provides a "higher order" `orWhere` method that allows you to fluently chain these scopes together without the use of closures: $users = App\Models\User::popular()->orWhere->active()->get(); @@ -1055,7 +1055,7 @@ After defining and mapping your Eloquent events, you may use [event listeners](h ### Using Closures -Instead of using custom event classes, you may register Closures that execute when various model events are fired. Typically, you should register these Closures in the `booted` method of your model: +Instead of using custom event classes, you may register closures that execute when various model events are fired. Typically, you should register these closures in the `booted` method of your model: ### Muting Events -You may occasionally wish to temporarily "mute" all events fired by a model. You may achieve this using the `withoutEvents` method. The `withoutEvents` method accepts a Closure as its only argument. Any code executed within this Closure will not fire model events. For example, the following will fetch and delete an `App\Models\User` instance without firing any model events. Any value returned by the given Closure will be returned by the `withoutEvents` method: +You may occasionally wish to temporarily "mute" all events fired by a model. You may achieve this using the `withoutEvents` method. The `withoutEvents` method accepts a closure as its only argument. Any code executed within this closure will not fire model events. For example, the following will fetch and delete an `App\Models\User` instance without firing any model events. Any value returned by the given closure will be returned by the `withoutEvents` method: use App\Models\User; diff --git a/errors.md b/errors.md index b43a7b2d4e..87c858d6bf 100644 --- a/errors.md +++ b/errors.md @@ -29,7 +29,7 @@ For local development, you should set the `APP_DEBUG` environment variable to `t All exceptions are handled by the `App\Exceptions\Handler` class. This class contains a `register` method where you may register custom exception reporter and renderer callbacks. We'll examine each of these concepts in detail. Exception reporting is used to log exceptions or send them to an external service like [Flare](https://flareapp.io), [Bugsnag](https://bugsnag.com) or [Sentry](https://github.com/getsentry/sentry-laravel). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. 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 `reportable` method to register a Closure that should be executed when an exception of a given type needs to be reported. Laravel will deduce what type of exception the Closure reports by examining the type-hint of the Closure: +For example, if you need to report different types of exceptions in different ways, you may use the `reportable` method to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will deduce what type of exception the closure reports by examining the type-hint of the closure: use App\Exceptions\CustomException; @@ -111,7 +111,7 @@ The `$dontReport` property of the exception handler contains an array of excepti ### Rendering Exceptions -By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering Closure for exceptions of a given type. You may accomplish this via the `renderable` method of your exception handler. Laravel will deduce what type of exception the Closure renders by examining the type-hint of the Closure: +By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this via the `renderable` method of your exception handler. Laravel will deduce what type of exception the closure renders by examining the type-hint of the closure: use App\Exceptions\CustomException; diff --git a/events.md b/events.md index 2aef00c126..4933bbbfc5 100644 --- a/events.md +++ b/events.md @@ -48,7 +48,7 @@ Of course, manually creating the files for each event and listener is cumbersome ### 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`: +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`: use App\Events\PodcastProcessed; @@ -67,7 +67,7 @@ Typically, events should be registered via the `EventServiceProvider` `$listen` #### Queueable Anonymous Event Listeners -When registering event listeners manually, you may wrap the listener Closure within the `Illuminate\Events\queueable` function to instruct Laravel to execute the listener using the [queue](/docs/{{version}}/queues): +When registering event listeners manually, you may wrap the listener closure within the `Illuminate\Events\queueable` function to instruct Laravel to execute the listener using the [queue](/docs/{{version}}/queues): use App\Events\PodcastProcessed; use function Illuminate\Events\queueable; @@ -91,7 +91,7 @@ Like queued jobs, you may use the `onConnection`, `onQueue`, and `delay` methods // })->onConnection('redis')->onQueue('podcasts')->delay(now()->addSeconds(10))); -If you would like to handle anonymous queued listener failures, you may provide a Closure to the `catch` method while defining the `queueable` listener: +If you would like to handle anonymous queued listener failures, you may provide a closure to the `catch` method while defining the `queueable` listener: use App\Events\PodcastProcessed; use function Illuminate\Events\queueable; diff --git a/filesystem.md b/filesystem.md index cc1329bebb..85ae480935 100644 --- a/filesystem.md +++ b/filesystem.md @@ -519,7 +519,7 @@ Next, you should create a [service provider](/docs/{{version}}/providers) such a } } -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. +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. Next, register the service provider in your `config/app.php` configuration file: diff --git a/helpers.md b/helpers.md index 051120d5da..e01dc79ce5 100644 --- a/helpers.md +++ b/helpers.md @@ -711,7 +711,7 @@ The `Arr::sort` method sorts an array by its values: // ['Chair', 'Desk', 'Table'] -You may also sort the array by the results of the given Closure: +You may also sort the array by the results of the given closure: use Illuminate\Support\Arr; @@ -759,7 +759,7 @@ The `Arr::sortRecursive` method recursively sorts an array using the `sort` func #### `Arr::where()` {#collection-method} -The `Arr::where` method filters an array using the given Closure: +The `Arr::where` method filters an array using the given closure: use Illuminate\Support\Arr; @@ -2072,7 +2072,7 @@ The `replaceMatches` method replaces all portions of a string matching a given p // '15015551000' -The `replaceMatches` method also accepts a Closure that will be invoked with each portion of the string matching the given party, allowing you to perform the replacement logic within the Closure and return the replaced value: +The `replaceMatches` method also accepts a closure that will be invoked with each portion of the string matching the given party, allowing you to perform the replacement logic within the closure and return the replaced value: use Illuminate\Support\Str; @@ -2248,7 +2248,7 @@ The `upper` method converts the given string to uppercase: #### `when` {#collection-method} -The `when` method invokes the given Closure if a given condition is true. The Closure will receive the fluent string instance: +The `when` method invokes the given closure if a given condition is true. The closure will receive the fluent string instance: use Illuminate\Support\Str; @@ -2259,12 +2259,12 @@ The `when` method invokes the given Closure if a given condition is true. The Cl // 'Taylor Otwell' -If necessary, you may pass another Closure as the third parameter to the `when` method. This Closure will execute if the condition parameter evaluates to `false`. +If necessary, you may pass another closure as the third parameter to the `when` method. This closure will execute if the condition parameter evaluates to `false`. #### `whenEmpty` {#collection-method} -The `whenEmpty` method invokes the given Closure if the string is empty. If the Closure returns a value, that value will also be returned by the `whenEmpty` method. If the Closure does not return a value, the fluent string instance will be returned: +The `whenEmpty` method invokes the given closure if the string is empty. If the closure returns a value, that value will also be returned by the `whenEmpty` method. If the closure does not return a value, the fluent string instance will be returned: use Illuminate\Support\Str; @@ -2655,7 +2655,7 @@ The `optional` function accepts any argument and allows you to access properties {!! old('name', optional($user)->name) !!} -The `optional` function also accepts a Closure as its second argument. The Closure will be invoked if the value provided as the first argument is not null: +The `optional` function also accepts a closure as its second argument. The closure will be invoked if the value provided as the first argument is not null: return optional(User::find($id), function ($user) { return $user->name; @@ -2698,13 +2698,13 @@ The `request` function returns the current [request](/docs/{{version}}/requests) #### `rescue()` {#collection-method} -The `rescue` function executes the given Closure and catches any exceptions that occur during its execution. All exceptions that are caught will be sent to your [exception handler](/docs/{{version}}/errors#the-exception-handler); however, the request will continue processing: +The `rescue` function executes the given closure and catches any exceptions that occur during its execution. All exceptions that are caught will be sent to your [exception handler](/docs/{{version}}/errors#the-exception-handler); however, the request will continue processing: return rescue(function () { return $this->method(); }); -You may also pass a second argument to the `rescue` function. This argument will be the "default" value that should be returned if an exception occurs while executing the Closure: +You may also pass a second argument to the `rescue` function. This argument will be the "default" value that should be returned if an exception occurs while executing the closure: return rescue(function () { return $this->method(); @@ -2761,7 +2761,7 @@ The session store will be returned if no value is passed to the function: #### `tap()` {#collection-method} -The `tap` function accepts two arguments: an arbitrary `$value` and a Closure. The `$value` will be passed to the Closure and then be returned by the `tap` function. The return value of the Closure is irrelevant: +The `tap` function accepts two arguments: an arbitrary `$value` and a closure. The `$value` will be passed to the closure and then be returned by the `tap` function. The return value of the closure is irrelevant: $user = tap(User::first(), function ($user) { $user->name = 'taylor'; @@ -2769,7 +2769,7 @@ The `tap` function accepts two arguments: an arbitrary `$value` and a Closure. T $user->save(); }); -If no Closure is passed to the `tap` function, you may call any method on the given `$value`. The return value of the method you call will always be `$value`, regardless of what the method actually returns in its definition. For example, the Eloquent `update` method typically returns an integer. However, we can force the method to return the model itself by chaining the `update` method call through the `tap` function: +If no closure is passed to the `tap` function, you may call any method on the given `$value`. The return value of the method you call will always be `$value`, regardless of what the method actually returns in its definition. For example, the Eloquent `update` method typically returns an integer. However, we can force the method to return the model itself by chaining the `update` method call through the `tap` function: $user = tap($user)->update([ 'name' => $name, @@ -2825,7 +2825,7 @@ The `trait_uses_recursive` function returns all traits used by a trait: #### `transform()` {#collection-method} -The `transform` function executes a `Closure` on a given value if the value is not [blank](#method-blank) and returns the result of the `Closure`: +The `transform` function executes a closure on a given value if the value is not [blank](#method-blank) and returns the result of the closure: $callback = function ($value) { return $value * 2; @@ -2835,7 +2835,7 @@ The `transform` function executes a `Closure` on a given value if the value is n // 10 -A default value or `Closure` may also be passed as the third parameter to the method. This value will be returned if the given value is blank: +A default value or closure may also be passed as the third parameter to the method. This value will be returned if the given value is blank: $result = transform(null, $callback, 'The value is blank'); @@ -2851,7 +2851,7 @@ The `validator` function creates a new [validator](/docs/{{version}}/validation) #### `value()` {#collection-method} -The `value` function returns 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: +The `value` function returns 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: $result = value(true); @@ -2873,7 +2873,7 @@ The `view` function retrieves a [view](/docs/{{version}}/views) instance: #### `with()` {#collection-method} -The `with` function returns the value it is given. If a `Closure` is passed as the second argument to the function, the `Closure` will be executed and its result will be returned: +The `with` function returns the value it is given. If a closure is passed as the second argument to the function, the closure will be executed and its result will be returned: $callback = function ($value) { return (is_numeric($value)) ? $value * 2 : 0; diff --git a/http-client.md b/http-client.md index 73f78d0bfe..0624a8dcbe 100644 --- a/http-client.md +++ b/http-client.md @@ -187,7 +187,7 @@ The `throw` method returns the response instance if no error occurred, allowing return Http::post(...)->throw()->json(); -If you would like to perform some additional logic before the exception is thrown, you may pass a Closure to the `throw` method. The exception will be thrown automatically after the Closure is invoked, so you do not need to re-throw the exception from within the Closure: +If you would like to perform some additional logic before the exception is thrown, you may pass a closure to the `throw` method. The exception will be thrown automatically after the closure is invoked, so you do not need to re-throw the exception from within the closure: return Http::post(...)->throw(function ($response, $e) { // @@ -217,7 +217,7 @@ For example, to instruct the HTTP client to return empty, `200` status code resp Http::fake(); $response = Http::post(...); - + > {note} When faking requests, HTTP client middleware are not executed. You should define expectations for faked responses as if these middleware have run correctly. diff --git a/mail.md b/mail.md index 38c551b64e..804e908f81 100644 --- a/mail.md +++ b/mail.md @@ -648,7 +648,7 @@ Sometimes you may wish to capture the HTML content of a mailable without sending ### Previewing Mailables In The Browser -When designing a mailable's template, it is convenient to quickly preview the rendered mailable in your browser like a typical Blade template. For this reason, Laravel allows you to return any mailable directly from a route Closure or controller. When a mailable is returned, it will be rendered and displayed in the browser, allowing you to quickly preview its design without needing to send it to an actual email address: +When designing a mailable's template, it is convenient to quickly preview the rendered mailable in your browser like a typical Blade template. For this reason, Laravel allows you to return any mailable directly from a route closure or controller. When a mailable is returned, it will be rendered and displayed in the browser, allowing you to quickly preview its design without needing to send it to an actual email address: Route::get('mailable', function () { $invoice = App\Models\Invoice::find(1); diff --git a/migrations.md b/migrations.md index 8749855460..817714462d 100644 --- a/migrations.md +++ b/migrations.md @@ -163,7 +163,7 @@ The `migrate:fresh` command will drop all tables from the database and then exec ### Creating Tables -To create a new database table, use the `create` method on the `Schema` facade. The `create` method accepts two arguments: the first is the name of the table, while the second is a `Closure` which receives a `Blueprint` object that may be used to define the new table: +To create a new database table, use the `create` method on the `Schema` facade. The `create` method accepts two arguments: the first is the name of the table, while the second is a closure which receives a `Blueprint` object that may be used to define the new table: Schema::create('users', function (Blueprint $table) { $table->id(); @@ -226,7 +226,7 @@ Before renaming a table, you should verify that any foreign key constraints on t ### Creating Columns -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: +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 (Blueprint $table) { $table->string('email'); diff --git a/notifications.md b/notifications.md index 9d0a507e27..4999a2c96e 100644 --- a/notifications.md +++ b/notifications.md @@ -431,7 +431,7 @@ The `attachData` method may be used to attach a raw string of bytes as an attach ### Previewing Mail Notifications -When designing a mail notification template, it is convenient to quickly preview the rendered mail message in your browser like a typical Blade template. For this reason, Laravel allows you to return any mail message generated by a mail notification directly from a route Closure or controller. When a `MailMessage` is returned, it will be rendered and displayed in the browser, allowing you to quickly preview its design without needing to send it to an actual email address: +When designing a mail notification template, it is convenient to quickly preview the rendered mail message in your browser like a typical Blade template. For this reason, Laravel allows you to return any mail message generated by a mail notification directly from a route closure or controller. When a `MailMessage` is returned, it will be rendered and displayed in the browser, allowing you to quickly preview its design without needing to send it to an actual email address: Route::get('mail', function () { $invoice = App\Invoice::find(1); diff --git a/packages.md b/packages.md index 1a0decede7..f72241e7ff 100644 --- a/packages.md +++ b/packages.md @@ -101,7 +101,7 @@ 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. +> {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 diff --git a/passwords.md b/passwords.md index 4a5b898cc2..e1c55567c4 100644 --- a/passwords.md +++ b/passwords.md @@ -126,7 +126,7 @@ Of course, we need to define a route to actually handle the password reset form Before moving on, let's examine this route in more detail. First, the request's `token`, `email`, and `password` attributes are validated. Next, we will use Laravel's built-in "password broker" (via the `Password` facade) to validate the password reset request credentials. -If the token, email address, and password given to the password broker are valid, the Closure passed to the `reset` method will be invoked. Within this Closure, which receives the user instance and the plain-text password, we may update the user's password in the database. +If the token, email address, and password given to the password broker are valid, the closure passed to the `reset` method will be invoked. Within this closure, which receives the user instance and the plain-text password, we may update the user's password in the database. The `reset` method returns a "status" slug. This status may be translated using Laravel's [localization](/docs/{{version}}/localization) helpers in order to display a user-friendly message to the user regarding the status of their request. The translation of the password reset status is determined by your application's `resources/lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. @@ -136,7 +136,7 @@ The `reset` method returns a "status" slug. This status may be translated using #### Reset Link Customization -You may customize the password reset link URL using the `createUrlUsing` method provided by the `ResetPassword` notification class. This method accepts a Closure which receives the user instance that is receiving the notification as well as the password reset link token. Typically, you should call this method from a service provider's `boot` method: +You may customize the password reset link URL using the `createUrlUsing` method provided by the `ResetPassword` notification class. This method accepts a closure which receives the user instance that is receiving the notification as well as the password reset link token. Typically, you should call this method from a service provider's `boot` method: use Illuminate\Auth\Notifications\ResetPassword; diff --git a/queries.md b/queries.md index 7a19458bb1..28324fc3da 100644 --- a/queries.md +++ b/queries.md @@ -107,7 +107,7 @@ If you would like to retrieve a Collection containing the values of a single col ### Chunking Results -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: +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) { foreach ($users as $user) { @@ -115,7 +115,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`: +You may stop further chunks from being processed by returning `false` from the closure: DB::table('users')->orderBy('id')->chunk(100, function ($users) { // Process the records... @@ -285,7 +285,7 @@ To perform a "cross join" use the `crossJoin` method with the name of the table #### Advanced Join Clauses -You may also specify more advanced join clauses. To get started, pass a `Closure` as the second argument into the `join` method. The `Closure` will receive a `JoinClause` object which allows you to specify constraints on the `join` clause: +You may also specify more advanced join clauses. To get started, pass a closure as the second argument into the `join` method. The closure will receive a `JoinClause` object which allows you to specify constraints on the `join` clause: DB::table('users') ->join('contacts', function ($join) { @@ -305,7 +305,7 @@ If you would like to use a "where" style clause on your joins, you may use the ` #### Subquery Joins -You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a query to a subquery. Each of these methods receive three arguments: the subquery, its table alias, and a Closure that defines the related columns: +You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a query to a subquery. Each of these methods receive three arguments: the subquery, its table alias, and a closure that defines the related columns: $latestPosts = DB::table('posts') ->select('user_id', DB::raw('MAX(created_at) as last_post_created_at')) @@ -379,7 +379,7 @@ You may chain where constraints together as well as add `or` clauses to the quer ->orWhere('name', 'John') ->get(); -If you need to group an "or" condition within parentheses, you may pass a Closure as the first argument to the `orWhere` method: +If you need to group an "or" condition within parentheses, you may pass a closure as the first argument to the `orWhere` method: $users = DB::table('users') ->where('votes', '>', 100) @@ -507,7 +507,7 @@ Sometimes you may need to create more advanced where clauses such as "where exis }) ->get(); -As you can see, passing a `Closure` into the `where` method instructs the query builder to begin a constraint group. The `Closure` will receive a query builder instance which you can use to set the constraints that should be contained within the parenthesis group. The example above will produce the following SQL: +As you can see, passing a closure into the `where` method instructs the query builder to begin a constraint group. The closure will receive a query builder instance which you can use to set the constraints that should be contained within the parenthesis group. The example above will produce the following SQL: select * from users where name = 'John' and (votes > 100 or title = 'Admin') @@ -516,7 +516,7 @@ As you can see, passing a `Closure` into the `where` method instructs the query ### Where Exists Clauses -The `whereExists` method allows you to write `where exists` SQL clauses. The `whereExists` method accepts a `Closure` argument, which will receive a query builder instance allowing you to define the query that should be placed inside of the "exists" clause: +The `whereExists` method allows you to write `where exists` SQL clauses. The `whereExists` method accepts a closure argument, which will receive a query builder instance allowing you to define the query that should be placed inside of the "exists" clause: $users = DB::table('users') ->whereExists(function ($query) { @@ -536,7 +536,7 @@ The query above will produce the following SQL: ### Subquery Where Clauses -Sometimes you may need to construct a where clause that compares the results of a subquery to a given value. You may accomplish this by passing a Closure and a value to the `where` method. For example, the following query will retrieve all users who have a recent "membership" of a given type; +Sometimes you may need to construct a where clause that compares the results of a subquery to a given value. You may accomplish this by passing a closure and a value to the `where` method. For example, the following query will retrieve all users who have a recent "membership" of a given type; use App\Models\User; @@ -681,9 +681,9 @@ Sometimes you may want clauses to apply to a query only when something else is t }) ->get(); -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. +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: +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; diff --git a/queues.md b/queues.md index 99cac15085..41189beee9 100644 --- a/queues.md +++ b/queues.md @@ -517,7 +517,7 @@ Alternatively, the `dispatchAfterResponse` method delays dispatching a job until SendNotification::dispatchAfterResponse(); -You may `dispatch` a Closure and chain the `afterResponse` method onto the helper to execute a Closure after the response has been sent to the browser: +You may `dispatch` a closure and chain the `afterResponse` method onto the helper to execute a closure after the response has been sent to the browser: use App\Mail\WelcomeMessage; use Illuminate\Support\Facades\Mail; @@ -568,7 +568,7 @@ Job chaining allows you to specify a list of queued jobs that should be run in s new ReleasePodcast, ])->dispatch(); -In addition to chaining job class instances, you may also chain Closures: +In addition to chaining job class instances, you may also chain closures: Bus::chain([ new ProcessPodcast, @@ -594,7 +594,7 @@ If you would like to specify the connection and queue that should be used for th #### Chain Failures -When chaining jobs, you may use the `catch` method to specify a Closure that should be invoked if a job within the chain fails. The given callback will receive the exception instance that caused the job failure: +When chaining jobs, you may use the `catch` method to specify a closure that should be invoked if a job within the chain fails. The given callback will receive the exception instance that caused the job failure: use Illuminate\Support\Facades\Bus; use Throwable; @@ -1035,7 +1035,7 @@ For convenience, Laravel provides a `queue:retry-batch` Artisan command that all ## Queueing Closures -Instead of dispatching a job class to the queue, you may also dispatch a Closure. This is great for quick, simple tasks that need to be executed outside of the current request cycle. When dispatching Closures to the queue, the Closure's code contents is cryptographically signed so it can not be modified in transit: +Instead of dispatching a job class to the queue, you may also dispatch a closure. This is great for quick, simple tasks that need to be executed outside of the current request cycle. When dispatching closures to the queue, the closure's code contents is cryptographically signed so it can not be modified in transit: $podcast = App\Podcast::find(1); @@ -1043,7 +1043,7 @@ Instead of dispatching a job class to the queue, you may also dispatch a Closure $podcast->publish(); }); -Using the `catch` method, you may provide a Closure that should be executed if the queued Closure fails to complete successfully after exhausting all of your queue's configured retry attempts: +Using the `catch` method, you may provide a closure that should be executed if the queued closure fails to complete successfully after exhausting all of your queue's configured retry attempts: use Throwable; @@ -1459,7 +1459,7 @@ 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: +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) { diff --git a/redis.md b/redis.md index 46eab443d9..852a62846c 100644 --- a/redis.md +++ b/redis.md @@ -226,7 +226,7 @@ This will give you an instance of the default Redis server. You may also pass th ### Pipelining Commands -Pipelining should be used when you need to send many commands to the server. The `pipeline` method accepts one argument: a `Closure` that receives a Redis instance. You may issue all of your commands to this Redis instance and they will all be streamed to the server thus providing better performance: +Pipelining should be used when you need to send many commands to the server. The `pipeline` method accepts one argument: a closure that receives a Redis instance. You may issue all of your commands to this Redis instance and they will all be streamed to the server thus providing better performance: Redis::pipeline(function ($pipe) { for ($i = 0; $i < 1000; $i++) { @@ -288,7 +288,7 @@ Now we may publish messages to the channel using the `publish` method: #### Wildcard Subscriptions -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`: +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) { echo $message; diff --git a/releases.md b/releases.md index 9e35f78f40..e2027c393a 100644 --- a/releases.md +++ b/releases.md @@ -176,7 +176,7 @@ _Rate limiting improvements were contributed by [Taylor Otwell](https://github.c Laravel's request rate limiter feature has been augmented with more flexibility and power, while still maintaining backwards compatibility with previous release's `throttle` middleware API. -Rate limiters are defined using the `RateLimiter` facade's `for` method. The `for` method accepts a rate limiter name and a Closure that returns the limit configuration that should apply to routes that are assigned this rate limiter: +Rate limiters are defined using the `RateLimiter` facade's `for` method. The `for` method accepts a rate limiter name and a closure that returns the limit configuration that should apply to routes that are assigned this rate limiter: use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Support\Facades\RateLimiter; @@ -246,7 +246,7 @@ For this reason, Laravel now allows you to pre-render a maintenance mode view th _Catch improvements were contributed by [Mohamed Said](https://github.com/themsaid)_. -Using the new `catch` method, you may now provide a Closure that should be executed if a queued Closure fails to complete successfully after exhausting all of your queue's configured retry attempts: +Using the new `catch` method, you may now provide a closure that should be executed if a queued closure fails to complete successfully after exhausting all of your queue's configured retry attempts: use Throwable; @@ -272,7 +272,7 @@ To learn more about Blade components, please consult the [Blade documentation](/ _Event listener improvements were contributed by [Taylor Otwell](https://github.com/taylorotwell)_. -Closure based event listeners may now be registered by only passing the Closure to the `Event::listen` method. Laravel will inspect the Closure to determine which type of event the listener handles: +Closure based event listeners may now be registered by only passing the closure to the `Event::listen` method. Laravel will inspect the closure to determine which type of event the listener handles: use App\Events\PodcastProcessed; use Illuminate\Support\Facades\Event; @@ -281,7 +281,7 @@ Closure based event listeners may now be registered by only passing the Closure // }); -In addition, Closure based event listeners may now be marked as queueable using the `Illuminate\Events\queueable` function: +In addition, closure based event listeners may now be marked as queueable using the `Illuminate\Events\queueable` function: use App\Events\PodcastProcessed; use function Illuminate\Events\queueable; @@ -297,7 +297,7 @@ Like queued jobs, you may use the `onConnection`, `onQueue`, and `delay` methods // })->onConnection('redis')->onQueue('podcasts')->delay(now()->addSeconds(10))); -If you would like to handle anonymous queued listener failures, you may provide a Closure to the `catch` method while defining the `queueable` listener: +If you would like to handle anonymous queued listener failures, you may provide a closure to the `catch` method while defining the `queueable` listener: use App\Events\PodcastProcessed; use function Illuminate\Events\queueable; diff --git a/requests.md b/requests.md index f0642f1b53..108bfb6fa1 100644 --- a/requests.md +++ b/requests.md @@ -15,7 +15,7 @@ ## Accessing The Request -To obtain an instance of the current HTTP request via dependency injection, you should type-hint the `Illuminate\Http\Request` class on your controller method. The incoming request instance will automatically be injected by the [service container](/docs/{{version}}/container): +To obtain an instance of the current HTTP request via dependency injection, you should type-hint the `Illuminate\Http\Request` class on your route callback or controller method. The incoming request instance will automatically be injected by the Laravel [service container](/docs/{{version}}/container): #### Accessing The Request Via Route Closures -You may also type-hint the `Illuminate\Http\Request` class on a route Closure. The service container will automatically inject the incoming request into the Closure when it is executed: +You may also type-hint the `Illuminate\Http\Request` class on a route closure. The service container will automatically inject the incoming request into the closure when it is executed: use Illuminate\Http\Request; @@ -130,7 +130,7 @@ The [PSR-7 standard](https://www.php-fig.org/psr/psr-7/) specifies interfaces fo composer require symfony/psr-http-message-bridge composer require nyholm/psr7 -Once you have installed these libraries, you may obtain a PSR-7 request by type-hinting the request interface on your route Closure or controller method: +Once you have installed these libraries, you may obtain a PSR-7 request by type-hinting the request interface on your route closure or controller method: use Psr\Http\Message\ServerRequestInterface; diff --git a/responses.md b/responses.md index e96ba0db31..1438a04f92 100644 --- a/responses.md +++ b/responses.md @@ -299,6 +299,6 @@ If you would like to define a custom response that you can re-use in a variety o } } -The `macro` function accepts a name as its first argument, and a Closure as its second. The macro's Closure will be executed when calling the macro name from a `ResponseFactory` implementation or the `response` helper: +The `macro` function accepts a name as its first argument, and a closure as its second. The macro's closure will be executed when calling the macro name from a `ResponseFactory` implementation or the `response` helper: return response()->caps('foo'); diff --git a/routing.md b/routing.md index a3b33ab361..2bdd6eaf02 100644 --- a/routing.md +++ b/routing.md @@ -28,7 +28,7 @@ ## Basic Routing -The most basic Laravel routes accept a URI and a `Closure`, providing a very simple and expressive method of defining routes and behavior without complicated routing configuration files: +The most basic Laravel routes accept a URI and a closure, providing a very simple and expressive method of defining routes and behavior without complicated routing configuration files: use Illuminate\Support\Facades\Route; @@ -457,7 +457,7 @@ If a matching model instance is not found in the database, a 404 HTTP response w #### Customizing The Resolution Logic -If you wish to define your own model binding 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. Again, this customization should take place in the `boot` method of your application's `RouteServiceProvider`: +If you wish to define your own model binding 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. Again, this customization should take place in the `boot` method of your application's `RouteServiceProvider`: use App\Models\User; use Illuminate\Support\Facades\Route; @@ -524,7 +524,7 @@ Using the `Route::fallback` method, you may define a route that will be executed Laravel includes powerful and customizable rate limiting services that you may utilize to restrict the amount of traffic for a given route or group of routes. To get started, you should define rate limiter configurations that meet your application's needs. Typically, this should be done within the `configureRateLimiting` method of your application's `App\Providers\RouteServiceProvider` class. -Rate limiters are defined using the `RateLimiter` facade's `for` method. The `for` method accepts a rate limiter name and a Closure that returns the limit configuration that should apply to routes that are assigned to the rate limiter. Limit configuration are instances of the `Illuminate\Cache\RateLimiting\Limit` class. This class contains helpful "builder" methods so that you can quickly define your limit. The rate limiter name may be any string you wish: +Rate limiters are defined using the `RateLimiter` facade's `for` method. The `for` method accepts a rate limiter name and a closure that returns the limit configuration that should apply to routes that are assigned to the rate limiter. Limit configuration are instances of the `Illuminate\Cache\RateLimiting\Limit` class. This class contains helpful "builder" methods so that you can quickly define your limit. The rate limiter name may be any string you wish: use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Support\Facades\RateLimiter; diff --git a/scheduling.md b/scheduling.md index cd30a535cc..9519ee3f78 100644 --- a/scheduling.md +++ b/scheduling.md @@ -40,7 +40,7 @@ Typically, you would not add a scheduler Cron entry to your local development ma ## Defining Schedules -You may define all of your scheduled tasks in the `schedule` method of the `App\Console\Kernel` class. To get started, let's look at an example of scheduling a task. In this example, we will schedule a `Closure` to be called every day at midnight. Within the `Closure` we will execute a database query to clear a table: +You may define all of your scheduled tasks in the `schedule` method of the `App\Console\Kernel` class. To get started, let's look at an example of scheduling a task. In this example, we will schedule a closure to be called every day at midnight. Within the closure we will execute a database query to clear a table: call(new DeleteRecentUsers)->daily(); ### Scheduling Artisan Commands -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: +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 Taylor --force')->daily(); @@ -91,7 +91,7 @@ In addition to scheduling Closure calls, you may also schedule [Artisan commands ### Scheduling Queued Jobs -The `job` method may be used to schedule a [queued job](/docs/{{version}}/queues). This method provides a convenient way to schedule jobs without using the `call` method to manually create Closures to queue the job: +The `job` method may be used to schedule a [queued job](/docs/{{version}}/queues). This method provides a convenient way to schedule jobs without using the `call` method to manually create closures to queue the job: $schedule->job(new Heartbeat)->everyFiveMinutes(); @@ -201,7 +201,7 @@ Similarly, the `unlessBetween` method can be used to exclude the execution of a #### 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: +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: $schedule->command('emails:send')->daily()->when(function () { return true; @@ -347,7 +347,7 @@ The `onSuccess` and `onFailure` methods allow you to specify code to be executed // The task failed... }); -If output is available from your command, you may access it in your `after`, `onSuccess` or `onFailure` hooks by type-hinting an `Illuminate\Support\Stringable` instance as `$output` on your hook's Closure definition: +If output is available from your command, you may access it in your `after`, `onSuccess` or `onFailure` hooks by type-hinting an `Illuminate\Support\Stringable` instance as `$output` on your hook's closure definition: use Illuminate\Support\Stringable; diff --git a/scout.md b/scout.md index dff7bd1793..0558c26eb3 100644 --- a/scout.md +++ b/scout.md @@ -474,6 +474,6 @@ If you would like to define a custom builder method, you may use the `macro` met } } -The `macro` function accepts a name as its first argument, and a Closure as its second. The macro's Closure will be executed when calling the macro name from a `Laravel\Scout\Builder` implementation: +The `macro` function accepts a name as its first argument, and a closure as its second. The macro's closure will be executed when calling the macro name from a `Laravel\Scout\Builder` implementation: App\Models\Order::search('Star Trek')->count(); diff --git a/session.md b/session.md index ed038d8285..0990d199f3 100644 --- a/session.md +++ b/session.md @@ -98,7 +98,7 @@ There are two primary ways of working with session data in Laravel: the global ` } } -When you retrieve an item from the session, you may also pass a default value as the second argument to the `get` method. This default value will be returned if the specified key does not exist in the session. If you pass a `Closure` as the default value to the `get` method and the requested key does not exist, the `Closure` will be executed and its result returned: +When you retrieve an item from the session, you may also pass a default value as the second argument to the `get` method. This default value will be returned if the specified key does not exist in the session. If you pass a closure as the default value to the `get` method and the requested key does not exist, the closure will be executed and its result returned: $value = $request->session()->get('key', 'default'); diff --git a/structure.md b/structure.md index 553f4954cc..b4adede0c3 100644 --- a/structure.md +++ b/structure.md @@ -74,7 +74,7 @@ The `web.php` file contains routes that the `RouteServiceProvider` places in the The `api.php` file contains routes that the `RouteServiceProvider` places in the `api` middleware group. These routes are intended to be stateless, so requests entering the application through these routes are intended to be authenticated [via tokens](/docs/{{version}}/sanctum) 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 `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 `channels.php` file is where you may register all of the [event broadcasting](/docs/{{version}}/broadcasting) channels that your application supports. diff --git a/validation.md b/validation.md index f4bf9105e1..f0f7b39625 100644 --- a/validation.md +++ b/validation.md @@ -1078,7 +1078,7 @@ The field under validation must be present in the input data and not empty. A fi The field under validation must be present and not empty if the _anotherfield_ field is equal to any _value_. -If you would like to construct a more complex condition for the `required_if` rule, you may use the `Rule::requiredIf` method. This methods accepts a boolean or a Closure. When passed a Closure, the Closure should return `true` or `false` to indicate if the field under validation is required: +If you would like to construct a more complex condition for the `required_if` rule, you may use the `Rule::requiredIf` method. This methods accepts a boolean or a closure. When passed a closure, the closure should return `true` or `false` to indicate if the field under validation is required: use Illuminate\Validation\Rule; @@ -1273,13 +1273,13 @@ Let's assume our web application is for game collectors. If a game collector reg 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 a list of 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: +The first argument passed to the `sometimes` method is the name of the field we are conditionally validating. The second argument is a list of 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) { return $input->games >= 100; }); -> {tip} The `$input` parameter passed to your `Closure` will be an instance of `Illuminate\Support\Fluent` and may be used to access your input and files. +> {tip} The `$input` parameter passed to your closure will be an instance of `Illuminate\Support\Fluent` and may be used to access your input and files. ## Validating Arrays @@ -1371,7 +1371,7 @@ Once the rule has been defined, you may attach it to a validator by passing an i ### Using Closures -If you only need the functionality of a custom rule once throughout your application, you may use a Closure instead of a rule object. The Closure receives the attribute's name, the attribute's value, and a `$fail` callback that should be called if validation fails: +If you only need the functionality of a custom rule once throughout your application, you may use a closure instead of a rule object. The closure receives the attribute's name, the attribute's value, and a `$fail` callback that should be called if validation fails: $validator = Validator::make($request->all(), [ 'title' => [ @@ -1422,9 +1422,9 @@ Another method of registering custom validation rules is using the `extend` meth } } -The custom validator Closure receives four arguments: the name of the `$attribute` being validated, the `$value` of the attribute, an array of `$parameters` passed to the rule, and the `Validator` instance. +The custom validator closure receives four arguments: the name of the `$attribute` being validated, the `$value` of the attribute, an array of `$parameters` passed to the rule, and the `Validator` instance. -You may also pass a class and method to the `extend` method instead of a Closure: +You may also pass a class and method to the `extend` method instead of a closure: Validator::extend('foo', 'FooValidator@validate'); diff --git a/views.md b/views.md index b2529f675a..d68d447d6f 100644 --- a/views.md +++ b/views.md @@ -142,7 +142,7 @@ For this example, let's register the view composers within a [service provider]( 'profile', 'App\Http\View\Composers\ProfileComposer' ); - // Using Closure based composers... + // Using closure based composers... View::composer('dashboard', function ($view) { // }); From cbb094c1180f90dbb70839e8b2772e4532bf6b71 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 15:58:39 -0600 Subject: [PATCH 057/274] wip --- requests.md | 139 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 54 deletions(-) diff --git a/requests.md b/requests.md index 108bfb6fa1..ca05cc20ce 100644 --- a/requests.md +++ b/requests.md @@ -2,9 +2,13 @@ - [Accessing The Request](#accessing-the-request) - [Request Path & Method](#request-path-and-method) + - [Request Headers](#request-headers) + - [Request IP Address](#request-ip) - [PSR-7 Requests](#psr7-requests) - [Input Trimming & Normalization](#input-trimming-and-normalization) -- [Retrieving Input](#retrieving-input) +- [Input](#input) + - [Retrieving Input](#retrieving-input) + - [Determining If Input Is Present](#determining-if-input-is-present) - [Old Input](#old-input) - [Cookies](#cookies) - [Files](#files) @@ -15,7 +19,7 @@ ## Accessing The Request -To obtain an instance of the current HTTP request via dependency injection, you should type-hint the `Illuminate\Http\Request` class on your route callback or controller method. The incoming request instance will automatically be injected by the Laravel [service container](/docs/{{version}}/container): +To obtain an instance of the current HTTP request via dependency injection, you should type-hint the `Illuminate\Http\Request` class on your route closure or controller method. The incoming request instance will automatically be injected by the Laravel [service container](/docs/{{version}}/container): #### Dependency Injection & Route Parameters @@ -71,45 +83,41 @@ You may still type-hint the `Illuminate\Http\Request` and access your `id` route } } - -#### Accessing The Request Via Route Closures - -You may also type-hint the `Illuminate\Http\Request` class on a route closure. The service container will automatically inject the incoming request into the closure when it is executed: - - use Illuminate\Http\Request; - - Route::get('/', function (Request $request) { - // - }); - ### Request Path & Method -The `Illuminate\Http\Request` instance provides a variety of methods for examining the HTTP request for your application and extends the `Symfony\Component\HttpFoundation\Request` class. We will discuss a few of the most important methods below. +The `Illuminate\Http\Request` instance provides a variety of methods for examining the incoming HTTP request and extends the `Symfony\Component\HttpFoundation\Request` class. We will discuss a few of the most important methods below. #### Retrieving The Request Path -The `path` method returns the request's path information. So, if the incoming request is targeted at `http://domain.com/foo/bar`, the `path` method will return `foo/bar`: +The `path` method returns the request's path information. So, if the incoming request is targeted at `http://example.com/foo/bar`, the `path` method will return `foo/bar`: $uri = $request->path(); + +#### Inspecting The Request Path / Route + The `is` method allows you to verify that the incoming request path matches a given pattern. You may use the `*` character as a wildcard when utilizing this method: if ($request->is('admin/*')) { // } +Using the `routeIs` method, you may determine if the incoming request has matched a [named route](/docs/{{version}}/routing#named-routes): + + if ($request->routeIs('admin.*')) { + // + } + #### Retrieving The Request URL To retrieve the full URL for the incoming request you may use the `url` or `fullUrl` methods. The `url` method will return the URL without the query string, while the `fullUrl` method includes the query string: - // Without Query String... $url = $request->url(); - // With Query String... - $url = $request->fullUrl(); + $urlWithQueryString = $request->fullUrl(); #### Retrieving The Request Method @@ -122,6 +130,32 @@ The `method` method will return the HTTP verb for the request. You may use the ` // } + +### Request Headers + +You may retrieve a request header from the `Illuminate\Http\Request` instance using the `header` method. If the header is not present on the request, `null` will be returned. However, the `header` method accepts an optional second argument that will be returned if the header is not present on the request: + + $value = $request->header('X-Header-Name'); + + $value = $request->header('X-Header-Name', 'default'); + +The `hasHeader` method may be used to determine if the request contains a given header: + + if ($request->hasHeader('X-Header-Name')) { + // + } + +For convenience, the `bearerToken` may be used to a bearer token from the `Authorization` header. If no such header is present, an empty string will be returned: + + $token = $request->bearerToken(); + + +### Request IP Address + +The `ip` method may be used to retrieve the IP address of the client that made the request to your application: + + $ipAddress = $request->ip(); + ### PSR-7 Requests @@ -143,17 +177,20 @@ Once you have installed these libraries, you may obtain a PSR-7 request by type- ## Input Trimming & Normalization -By default, Laravel includes the `TrimStrings` and `ConvertEmptyStringsToNull` middleware in your application's global middleware stack. These middleware are listed in the stack by the `App\Http\Kernel` class. These middleware will automatically trim all incoming string fields on the request, as well as convert any empty string fields to `null`. This allows you to not have to worry about these normalization concerns in your routes and controllers. +By default, Laravel includes the `App\Http\Middleware\TrimStrings` and `App\Http\Middleware\ConvertEmptyStringsToNull` middleware in your application's global middleware stack. These middleware are listed in the global middleware stack by the `App\Http\Kernel` class. These middleware will automatically trim all incoming string fields on the request, as well as convert any empty string fields to `null`. This allows you to not have to worry about these normalization concerns in your routes and controllers. If you would like to disable this behavior, you may remove the two middleware from your application's middleware stack by removing them from the `$middleware` property of your `App\Http\Kernel` class. + +## Input + -## Retrieving Input +### Retrieving Input #### Retrieving All Input Data -You may also retrieve all of the input data as an `array` using the `all` method: +You may also retrieve all of the incoming request's input data as an `array` using the `all` method. This method may be used regardless of whether the incoming request is from an HTML form or is an XHR request: $input = $request->all(); @@ -193,19 +230,10 @@ You may call the `query` method without any arguments in order to retrieve all o $query = $request->query(); - -#### Retrieving Input Via Dynamic Properties - -You may also access user input using dynamic properties on the `Illuminate\Http\Request` instance. For example, if one of your application's forms contains a `name` field, you may access the value of the field like so: - - $name = $request->name; - -When using dynamic properties, Laravel will first look for the parameter's value in the request payload. If it is not present, Laravel will search for the field in the route parameters. - #### Retrieving JSON Input Values -When sending JSON requests to your application, you may access the JSON data via the `input` method as long as the `Content-Type` header of the request is properly set to `application/json`. You may even use "dot" syntax to dig into JSON arrays: +When sending JSON requests to your application, you may access the JSON data via the `input` method as long as the `Content-Type` header of the request is properly set to `application/json`. You may even use "dot" syntax to retrieve values that are nested within JSON arrays: $name = $request->input('user.name'); @@ -216,6 +244,15 @@ When dealing with HTML elements like checkboxes, your application may receive "t $archived = $request->boolean('archived'); + +#### Retrieving Input Via Dynamic Properties + +You may also access user input using dynamic properties on the `Illuminate\Http\Request` instance. For example, if one of your application's forms contains a `name` field, you may access the value of the field like so: + + $name = $request->name; + +When using dynamic properties, Laravel will first look for the parameter's value in the request payload. If it is not present, Laravel will search for the field in the matched route's parameters. + #### Retrieving A Portion Of The Input Data @@ -229,12 +266,12 @@ If you need to retrieve a subset of the input data, you may use the `only` and ` $input = $request->except('credit_card'); -> {tip} The `only` method returns all of the key / value pairs that you request; however, it will not return key / value pairs that are not present on the request. +> {note} The `only` method returns all of the key / value pairs that you request; however, it will not return key / value pairs that are not present on the request. - -#### Determining If An Input Value Is Present + +### Determining If Input Is Present -You should use the `has` method to determine if a value is present on the request. The `has` method returns `true` if the value is present on the request: +You may use the `has` method to determine if a value is present on the request. The `has` method returns `true` if the value is present on the request: if ($request->has('name')) { // @@ -246,7 +283,7 @@ When given an array, the `has` method will determine if all of the specified val // } -The `whenHas` method will execute the given callback if a value is present on the request: +The `whenHas` method will execute the given closure if a value is present on the request: $request->whenHas('name', function ($input) { // @@ -264,7 +301,7 @@ If you would like to determine if a value is present on the request and is not e // } -The `whenFilled` method will execute the given callback if a value is present on the request and is not empty: +The `whenFilled` method will execute the given closure if a value is present on the request and is not empty: $request->whenFilled('name', function ($input) { // @@ -279,7 +316,7 @@ To determine if a given key is absent from the request, you may use the `missing ### Old Input -Laravel allows you to keep input from one request during the next request. This feature is particularly useful for re-populating forms after detecting validation errors. However, if you are using Laravel's included [validation features](/docs/{{version}}/validation), it is unlikely you will need to manually use these methods, as some of Laravel's built-in validation facilities will call them automatically. +Laravel allows you to keep input from one request during the next request. This feature is particularly useful for re-populating forms after detecting validation errors. However, if you are using Laravel's included [validation features](/docs/{{version}}/validation), it is possible that you will not need to manually use these session input flashing methods directly, as some of Laravel's built-in validation facilities will call them automatically. #### Flashing Input To The Session @@ -301,6 +338,8 @@ Since you often will want to flash input to the session and then redirect to the return redirect('form')->withInput(); + return redirect()->route('user.create')->withInput(); + return redirect('form')->withInput( $request->except('password') ); @@ -312,7 +351,7 @@ To retrieve flashed input from the previous request, use the `old` method on the $username = $request->old('username'); -Laravel also provides a global `old` helper. If you are displaying old input within a [Blade template](/docs/{{version}}/blade), it is more convenient to use the `old` helper. If no old input exists for the given field, `null` will be returned: +Laravel also provides a global `old` helper. If you are displaying old input within a [Blade template](/docs/{{version}}/blade), it is more convenient to use the `old` helper to re-populate the form. If no old input exists for the given field, `null` will be returned: @@ -326,12 +365,6 @@ All cookies created by the Laravel framework are encrypted and signed with an au $value = $request->cookie('name'); -Alternatively, you may use the `Cookie` facade to access cookie values: - - use Illuminate\Support\Facades\Cookie; - - $value = Cookie::get('name'); - #### Attaching Cookies To Responses @@ -347,16 +380,16 @@ The `cookie` method also accepts a few more arguments which are used less freque 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly ); -Alternatively, you can use the `Cookie` facade to "queue" cookies for attachment to the outgoing response from your application. The `queue` method accepts a `Cookie` instance or the arguments needed to create a `Cookie` instance. These cookies will be attached to the outgoing response before it is sent to the browser: +If you would like to ensure that a cookie is sent with the outgoing response but you do not yet have an instance of that response, you can use the `Cookie` facade to "queue" cookies for attachment to the response when it is sent. The `queue` method accepts the arguments needed to create a cookie instance. These cookies will be attached to the outgoing response before it is sent to the browser: - Cookie::queue(Cookie::make('name', 'value', $minutes)); + use Illuminate\Support\Facades\Cookie; Cookie::queue('name', 'value', $minutes); #### Generating Cookie Instances -If you would like to generate a `Symfony\Component\HttpFoundation\Cookie` instance that can be given to a response instance at a later time, you may use the global `cookie` helper. This cookie will not be sent back to the client unless it is attached to a response instance: +If you would like to generate a `Symfony\Component\HttpFoundation\Cookie` instance that can be attached to a response instance at a later time, you may use the global `cookie` helper. This cookie will not be sent back to the client unless it is attached to a response instance: $cookie = cookie('name', 'value', $minutes); @@ -365,15 +398,13 @@ If you would like to generate a `Symfony\Component\HttpFoundation\Cookie` instan #### Expiring Cookies Early -You may remove a cookie by expiring it via the `forget` method of the `Cookie` facade: +You may remove a cookie by expiring it via the `withoutCookie` method of an outgoing response: - Cookie::queue(Cookie::forget('name')); - -Alternatively, you may attach the expired cookie to a response instance: + return response('Hello World')->withoutCookie('name'); - $cookie = Cookie::forget('name'); +If you do not yet have an instance of the outgoing response, you may use the `Cookie` facade's `queue` method to expire a cookie: - return response('Hello World')->withCookie($cookie); + Cookie::queue(Cookie::forget('name')); ## Files From 20c4555d80e174e89239b9cd25448fca164261f6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 16:03:32 -0600 Subject: [PATCH 058/274] wip --- requests.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/requests.md b/requests.md index ca05cc20ce..202993e3fd 100644 --- a/requests.md +++ b/requests.md @@ -412,7 +412,7 @@ If you do not yet have an instance of the outgoing response, you may use the `Co ### Retrieving Uploaded Files -You may access uploaded files from an `Illuminate\Http\Request` instance using the `file` method or using dynamic properties. The `file` method returns an instance of the `Illuminate\Http\UploadedFile` class, which extends the PHP `SplFileInfo` class and provides a variety of methods for interacting with the file: +You may retrieve uploaded files from an `Illuminate\Http\Request` instance using the `file` method or using dynamic properties. The `file` method returns an instance of the `Illuminate\Http\UploadedFile` class, which extends the PHP `SplFileInfo` class and provides a variety of methods for interacting with the file: $file = $request->file('photo'); @@ -450,7 +450,7 @@ There are a variety of other methods available on `UploadedFile` instances. Chec ### Storing Uploaded Files -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. +To store an uploaded file, you will typically use one of your configured [filesystems](/docs/{{version}}/filesystem). The `UploadedFile` class has a `store` method that will move an uploaded file to one of your disks, which may be a location on your local filesystem or 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 a unique ID will automatically be generated to serve as the file name. @@ -466,10 +466,12 @@ If you do not want a file name to be automatically generated, you may use the `s $path = $request->photo->storeAs('images', 'filename.jpg', 's3'); +> {tip} For more information about file storage in Laravel, check out the complete [file storage documentation](/docs/{{version}}/filesystem). + ## Configuring Trusted Proxies -When running your applications behind a load balancer that terminates TLS / SSL certificates, you may notice your application sometimes does not generate HTTPS links. Typically this is because your application is being forwarded traffic from your load balancer on port 80 and does not know it should generate secure links. +When running your applications behind a load balancer that terminates TLS / SSL certificates, you may notice your application sometimes does not generate HTTPS links when using the `url` helper. Typically this is because your application is being forwarded traffic from your load balancer on port 80 and does not know it should generate secure links. To solve this, you may use the `App\Http\Middleware\TrustProxies` middleware that is included in your Laravel application, which allows you to quickly customize the load balancers or proxies that should be trusted by your application. Your trusted proxies should be listed as an array on the `$proxies` property of this middleware. In addition to configuring the trusted proxies, you may configure the proxy `$headers` that should be trusted: From 244accceb070960731d619fbcedd8ba3c5cbaaf6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 12 Nov 2020 16:25:23 -0600 Subject: [PATCH 059/274] wip --- requests.md | 41 ----------------------- responses.md | 93 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 63 insertions(+), 71 deletions(-) diff --git a/requests.md b/requests.md index 202993e3fd..a8f30922f8 100644 --- a/requests.md +++ b/requests.md @@ -365,47 +365,6 @@ All cookies created by the Laravel framework are encrypted and signed with an au $value = $request->cookie('name'); - -#### Attaching Cookies To Responses - -You may attach a cookie to an outgoing `Illuminate\Http\Response` instance using the `cookie` method. You should pass the name, value, and number of minutes the cookie should be considered valid to this method: - - return response('Hello World')->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](https://secure.php.net/manual/en/function.setcookie.php) method: - - return response('Hello World')->cookie( - 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly - ); - -If you would like to ensure that a cookie is sent with the outgoing response but you do not yet have an instance of that response, you can use the `Cookie` facade to "queue" cookies for attachment to the response when it is sent. The `queue` method accepts the arguments needed to create a cookie instance. These cookies will be attached to the outgoing response before it is sent to the browser: - - use Illuminate\Support\Facades\Cookie; - - Cookie::queue('name', 'value', $minutes); - - -#### Generating Cookie Instances - -If you would like to generate a `Symfony\Component\HttpFoundation\Cookie` instance that can be attached to a response instance at a later time, you may use the global `cookie` helper. This cookie will not be sent back to the client unless it is attached to a response instance: - - $cookie = cookie('name', 'value', $minutes); - - return response('Hello World')->cookie($cookie); - - -#### Expiring Cookies Early - -You may remove a cookie by expiring it via the `withoutCookie` method of an outgoing response: - - return response('Hello World')->withoutCookie('name'); - -If you do not yet have an instance of the outgoing response, you may use the `Cookie` facade's `queue` method to expire a cookie: - - Cookie::queue(Cookie::forget('name')); - ## Files diff --git a/responses.md b/responses.md index 1438a04f92..3402842ed0 100644 --- a/responses.md +++ b/responses.md @@ -43,13 +43,24 @@ Typically, you won't just be returning simple strings or arrays from your route Returning a full `Response` instance allows you to customize the response's HTTP status code and headers. A `Response` instance inherits from the `Symfony\Component\HttpFoundation\Response` class, which provides a variety of methods for building HTTP responses: - Route::get('home', function () { + Route::get('/home', function () { return response('Hello World', 200) ->header('Content-Type', 'text/plain'); }); + +#### Eloquent Models & Collections + +You may also return [Eloquent ORM](/docs/{{version}}/eloquent) models and collections directly from your routes and controllers. When you do, Laravel will automatically convert the models and collections to JSON responses while respecting the model's [hidden attributes](/docs/{{version}}/eloquent-serialization#hiding-attributes-from-json): + + use App\Models\User; + + Route::get('/user/{user}', function (User $user) { + return $user; + }); + -#### Attaching Headers To Responses +### Attaching Headers To Responses Keep in mind that most response methods are chainable, allowing for the fluent construction of response instances. For example, you may use the `header` method to add a series of headers to the response before sending it back to the user: @@ -73,36 +84,58 @@ Or, you may use the `withHeaders` method to specify an array of headers to be ad Laravel includes a `cache.headers` middleware, which may be used to quickly set the `Cache-Control` header for a group of routes. If `etag` is specified in the list of directives, an MD5 hash of the response content will automatically be set as the ETag identifier: Route::middleware('cache.headers:public;max_age=2628000;etag')->group(function () { - Route::get('privacy', function () { + Route::get('/privacy', function () { // ... }); - Route::get('terms', function () { + Route::get('/terms', function () { // ... }); }); -#### Attaching Cookies To Responses +### Attaching Cookies To Responses -The `cookie` method on response instances allows you to easily attach cookies to the response. For example, you may use the `cookie` method to generate a cookie and fluently attach it to the response instance like so: +You may attach a cookie to an outgoing `Illuminate\Http\Response` instance using the `cookie` method. You should pass the name, value, and number of minutes the cookie should be considered valid to this method: - return response($content) - ->header('Content-Type', $type) - ->cookie('name', 'value', $minutes); + return response('Hello World')->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](https://secure.php.net/manual/en/function.setcookie.php) method: - ->cookie($name, $value, $minutes, $path, $domain, $secure, $httpOnly) + return response('Hello World')->cookie( + 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly + ); -Alternatively, you can use the `Cookie` facade to "queue" cookies for attachment to the outgoing response from your application. The `queue` method accepts a `Cookie` instance or the arguments needed to create a `Cookie` instance. These cookies will be attached to the outgoing response before it is sent to the browser: +If you would like to ensure that a cookie is sent with the outgoing response but you do not yet have an instance of that response, you can use the `Cookie` facade to "queue" cookies for attachment to the response when it is sent. The `queue` method accepts the arguments needed to create a cookie instance. These cookies will be attached to the outgoing response before it is sent to the browser: - Cookie::queue(Cookie::make('name', 'value', $minutes)); + use Illuminate\Support\Facades\Cookie; Cookie::queue('name', 'value', $minutes); + +#### Generating Cookie Instances + +If you would like to generate a `Symfony\Component\HttpFoundation\Cookie` instance that can be attached to a response instance at a later time, you may use the global `cookie` helper. This cookie will not be sent back to the client unless it is attached to a response instance: + + $cookie = cookie('name', 'value', $minutes); + + return response('Hello World')->cookie($cookie); + + +#### Expiring Cookies Early + +You may remove a cookie by expiring it via the `withoutCookie` method of an outgoing response: + + return response('Hello World')->withoutCookie('name'); + +If you do not yet have an instance of the outgoing response, you may use the `Cookie` facade's `queue` method to expire a cookie: + + Cookie::queue(Cookie::forget('name')); + -#### Cookies & Encryption +### Cookies & Encryption By default, all cookies generated by Laravel are encrypted and signed so that they can't be modified or read by the client. If you would like to disable encryption for a subset of cookies generated by your application, you may use the `$except` property of the `App\Http\Middleware\EncryptCookies` middleware, which is located in the `app/Http/Middleware` directory: @@ -120,13 +153,13 @@ By default, all cookies generated by Laravel are encrypted and signed so that th Redirect responses are instances of the `Illuminate\Http\RedirectResponse` class, and contain the proper headers needed to redirect the user to another URL. There are several ways to generate a `RedirectResponse` instance. The simplest method is to use the global `redirect` helper: - Route::get('dashboard', function () { + Route::get('/dashboard', function () { return redirect('home/dashboard'); }); -Sometimes you may wish to redirect the user to their previous location, such as when a submitted form is invalid. You may do so by using the global `back` helper function. Since this feature utilizes the [session](/docs/{{version}}/session), make sure the route calling the `back` function is using the `web` middleware group or has all of the session middleware applied: +Sometimes you may wish to redirect the user to their previous location, such as when a submitted form is invalid. You may do so by using the global `back` helper function. Since this feature utilizes the [session](/docs/{{version}}/session), make sure the route calling the `back` function is using the `web` middleware group: - Route::post('user/profile', function () { + Route::post('/user/profile', function () { // Validate the request... return back()->withInput(); @@ -141,7 +174,7 @@ When you call the `redirect` helper with no parameters, an instance of `Illumina If your route has parameters, you may pass them as the second argument to the `route` method: - // For a route with the following URI: profile/{id} + // For a route with the following URI: /profile/{id} return redirect()->route('profile', ['id' => 1]); @@ -150,11 +183,11 @@ If your route has parameters, you may pass them as the second argument to the `r If you are redirecting to a route with an "ID" parameter that is being populated from an Eloquent model, you may pass the model itself. The ID will be extracted automatically: - // For a route with the following URI: profile/{id} + // For a route with the following URI: /profile/{id} return redirect()->route('profile', [$user]); -If you would like to customize the value that is placed in the route parameter, you can specify the column in the route parameter definition (`profile/{id:slug}`) or you can override the `getRouteKey` method on your Eloquent model: +If you would like to customize the value that is placed in the route parameter, you can specify the column in the route parameter definition (`/profile/{id:slug}`) or you can override the `getRouteKey` method on your Eloquent model: /** * Get the value of the model's route key. @@ -171,9 +204,9 @@ If you would like to customize the value that is placed in the route parameter, You may also generate redirects to [controller actions](/docs/{{version}}/controllers). To do so, pass the controller and action name to the `action` method: - use App\Http\Controllers\HomeController; + use App\Http\Controllers\UserController; - return redirect()->action([HomeController::class, 'index']); + return redirect()->action([UserController::class, 'index']); If your controller route requires parameters, you may pass them as the second argument to the `action` method: @@ -193,8 +226,8 @@ Sometimes you may need to redirect to a domain outside of your application. You Redirecting to a new URL and [flashing data to the session](/docs/{{version}}/session#flash-data) are usually done at the same time. Typically, this is done after successfully performing an action when you flash a success message to the session. For convenience, you may create a `RedirectResponse` instance and flash data to the session in a single, fluent method chain: - Route::post('user/profile', function () { - // Update the user's profile... + Route::post('/user/profile', function () { + // ... return redirect('dashboard')->with('status', 'Profile updated!'); }); @@ -221,7 +254,7 @@ If you need control over the response's status and headers but also need to retu ->view('hello', $data, 200) ->header('Content-Type', $type); -Of course, if you do not need to pass a custom HTTP status code or custom headers, you should use the global `view` helper function. +Of course, if you do not need to pass a custom HTTP status code or custom headers, you may use the global `view` helper function. ### JSON Responses @@ -248,8 +281,6 @@ The `download` method may be used to generate a response that forces the user's return response()->download($pathToFile, $name, $headers); - return response()->download($pathToFile)->deleteFileAfterSend(); - > {note} Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII file name. @@ -257,6 +288,8 @@ The `download` method may be used to generate a response that forces the user's Sometimes you may wish to turn the string response of a given operation into a downloadable response without having to write the contents of the operation to disk. You may use the `streamDownload` method in this scenario. This method accepts a callback, file name, and an optional array of headers as its arguments: + use App\Services\GitHub; + return response()->streamDownload(function () { echo GitHub::api('repo') ->contents() @@ -275,7 +308,7 @@ The `file` method may be used to display a file, such as an image or PDF, direct ## Response Macros -If you would like to define a custom response that you can re-use in a variety of your routes and controllers, you may use the `macro` method on the `Response` facade. For example, from a [service provider's](/docs/{{version}}/providers) `boot` method: +If you would like to define a custom response that you can re-use in a variety of your routes and controllers, you may use the `macro` method on the `Response` facade. Typically, you should call this method from the `boot` method of one of your application's [service providers](/docs/{{version}}/providers), such as the `App\Providers\AppProvider` service provider: caps('foo'); From 8197773d17a97864be869a32e0d541efd78ffc53 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 13 Nov 2020 07:51:27 -0600 Subject: [PATCH 060/274] wip --- installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installation.md b/installation.md index 71f585eebc..6d2829d0f9 100644 --- a/installation.md +++ b/installation.md @@ -224,7 +224,7 @@ When using Laravel Sail, your application is executing within a Docker container ./sail npm run prod ``` -When reading the Laravel documentation, you will often see references to Artisan and Composer commands that do not reference Sail. Those examples assume that PHP is installed on your local computer. If you are using Sail for your local Laravel development environment, you should execute those commands using Sail: +**When reading the Laravel documentation, you will often see references to Artisan and Composer commands that do not reference Sail.** Those examples assume that PHP is installed on your local computer. If you are using Sail for your local Laravel development environment, you should execute those commands using Sail: ```bash // Running Artisan commands locally... From c6e712a1a77ce49061bed552c40c2e807a842e82 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 13 Nov 2020 08:10:18 -0600 Subject: [PATCH 061/274] add route example --- routing.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routing.md b/routing.md index 2bdd6eaf02..b1680c2640 100644 --- a/routing.md +++ b/routing.md @@ -187,6 +187,10 @@ For convenience, some commonly used regular expression patterns have helper meth // })->whereNumeric('id')->whereAlpha('name'); + Route::get('/user/{name}', function ($name) { + // + })->whereAlphaNumeric('name'); + Route::get('/user/{id}', function ($id) { // })->whereUuid('id'); From ccd37ca5ecf42e8850d1ddb3e9f98c19674b6338 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 13 Nov 2020 13:58:18 -0600 Subject: [PATCH 062/274] wip --- installation.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/installation.md b/installation.md index 6d2829d0f9..b4ebdab8b9 100644 --- a/installation.md +++ b/installation.md @@ -13,6 +13,7 @@ - [Starting & Stopping](#starting-and-stopping-sail) - [Executing Commands](#executing-sail-commands) - [Interacting With Databases](#interacting-with-sail-databases) + - [Running Tests](#running-tests) - [Adding Additional Services](#adding-additional-sail-services) - [Container CLI](#sail-container-cli) - [Customization](#sail-customization) @@ -251,6 +252,42 @@ Your application's `docker-compose.yml` file also contains an entry for a [Redis To connect to your application's Redis database from your local machine, you may use a graphical database management application such as [TablePlus](https://tableplus.com). By default, the Redis database is accessible at `localhost` port `6379`. + +### Running Tests + +Laravel provides amazing testing support out of the box, and you may use Sail's `test` command to run your applications [feature and unit tests](/docs/{{version}}/testing). Any CLI options that are accepted by PHPUnit may also be passed to the `test` command: + + ./sail test + + ./sail test --group orders + +The Sail `test` command is equivalent to running the `test` Artisan command: + + ./sail artisan test + + +#### Laravel Dusk + +[Laravel Dusk](/docs/{{version}}/dusk) provides an expressive, easy-to-use browser automation and testing API. Thanks to Sail, you may run these tests without ever installing Selenium or other tools on your local computer. To get started, uncomment the Selenium service in your application's `docker-compose.yml` file: + + selenium: + image: 'selenium/standalone-chrome' + volumes: + - '/dev/shm:/dev/shm' + networks: + - sail + +Next, ensure that the `laravel.test` service in your application's `docker-compose.yml` file has a `depends_on` entry for `selenium`: + + depends_on: + - mysql + - redis + - selenium + +Finally, you may run your Dusk test suite by starting Sail and running the `dusk` command: + + ./sail dusk + ### Adding Additional Services From 3100520770e069fa9a8b4f03f500a2fbad1643b0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 13 Nov 2020 15:18:20 -0600 Subject: [PATCH 063/274] wip --- views.md | 113 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 42 deletions(-) diff --git a/views.md b/views.md index d68d447d6f..272c457eb1 100644 --- a/views.md +++ b/views.md @@ -1,17 +1,22 @@ # Views -- [Creating Views](#creating-views) +- [Introduction](#introduction) +- [Creating & Rendering Views](#creating-and-rendering-views) + - [Nested Views](#nested-views) + - [Creating The First Available View](#creating-the-first-available-view) + - [Determining If A View Exists](#determining-if-a-view-exists) - [Passing Data To Views](#passing-data-to-views) - [Sharing Data With All Views](#sharing-data-with-all-views) - [View Composers](#view-composers) + - [View Creators](#view-creators) - [Optimizing Views](#optimizing-views) - -## Creating Views + +## Introduction > {tip} Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started. -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: +Of course, it's not practical to return entire HTML documents strings directly from your routes and controllers. Thankfully, views provide a convenient way to place all of our HTML in separate files. Views separate your controller / application logic from your presentation logic and are stored in the `resources/views` directory. A simple view might look something like this: @@ -27,53 +32,73 @@ Since this view is stored at `resources/views/greeting.blade.php`, we may return return view('greeting', ['name' => 'James']); }); + +## Creating & Rendering Views + +You may create a view by placing a file with the `.blade.php` extension in your application's `resources/views` directory. The `.blade.php` extension informs the framework that the file contains a [Blade template](/docs/{{version}}/blade). Blade templates contain HTML as well as Blade directives that allow you to easily echo values, create "if" statements, iterate over data, and more. + +Once you have created a view, you may return it from one of your application's routes or controllers using the global `view` helper: + + Route::get('/', function () { + return view('greeting', ['name' => 'James']); + }); + +Views may also be returned using the `View` facade: + + use Illuminate\Support\Facades\View; + + return View::make('greeting', ['name' => 'James']); + 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). -Views may also be nested within subdirectories 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: + +### Nested Views + +Views may also be nested within subdirectories 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 return it from one of your application's routes / controllers like so: return view('admin.profile', $data); > {note} View directory names should not contain the `.` character. - -#### Determining If A View Exists + +### Creating The First Available View -If you need to determine if a view exists, you may use the `View` facade. The `exists` method will return `true` if the view exists: +Using the `View` facade's `first` method, you may create the first view that exists in a given array of views. This may be useful if your application or package allows views to be customized or overwritten: use Illuminate\Support\Facades\View; - if (View::exists('emails.customer')) { - // - } - - -#### Creating The First Available View - -Using the `first` method, you may create the first view that exists in a given array of views. This is useful if your application or package allows views to be customized or overwritten: + return View::first(['custom.admin', 'admin'], $data); - return view()->first(['custom.admin', 'admin'], $data); + +### Determining If A View Exists -You may also call this method via the `View` [facade](/docs/{{version}}/facades): +If you need to determine if a view exists, you may use the `View` facade. The `exists` method will return `true` if the view exists: use Illuminate\Support\Facades\View; - return View::first(['custom.admin', 'admin'], $data); + if (View::exists('emails.customer')) { + // + } ## Passing Data To Views -As you saw in the previous examples, you may pass an array of data to views: +As you saw in the previous examples, you may pass an array of data to views to make that data available to the view: return view('greetings', ['name' => 'Victoria']); -When passing information in this manner, the data should be an array with key / value pairs. Inside your view, you can then access each value using its corresponding key, such as ``. As an alternative to passing a complete array of data to the `view` helper function, you may use the `with` method to add individual pieces of data to the view: +When passing information in this manner, the data should be an array with key / value pairs. After providing data to a view, you can then access each value within your view using the data's keys, such as ``. - return view('greeting')->with('name', 'Victoria'); +As an alternative to passing a complete array of data to the `view` helper function, you may use the `with` method to add individual pieces of data to the view. The `with` method returns an instance of the view object so that you can continue chaining methods before returning the view: + + return view('greeting') + ->with('name', 'Victoria') + ->with('occupation', 'Astronaut'); -#### Sharing Data With All Views +### Sharing Data With All Views -Occasionally, you may need to share a piece of data with all views that are rendered by your application. You may do so using the view facade's `share` method. Typically, you should place calls to `share` within a service provider's `boot` method. You are free to add them to the `AppServiceProvider` or generate a separate service provider to house them: +Occasionally, you may need to share data with all views that are rendered by your application. You may do so using the `View` facade's `share` method. Typically, you should place calls to the `share` method within a service provider's `boot` method. You are free to add them to the `App\Providers\AppServiceProvider` class or generate a separate service provider to house them: ## View Composers -View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view each time that view is rendered, a view composer can help you organize that logic into a single location. +View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view each time that view is rendered, a view composer can help you organize that logic into a single location. View composers may prove particularly useful if the same view is returned by multiple routes or controllers within your application and always needs a particular piece of data. + +Typically, view composers will be registered within one of your application's [service providers](/docs/{{version}}/providers). In this example, we'll assume that we have created a new `App\Providers\ViewServiceProvider` to house this logic. -For this example, let's register the view composers within a [service provider](/docs/{{version}}/providers). We'll use the `View` facade to access the underlying `Illuminate\Contracts\View\Factory` contract implementation. Remember, Laravel does not include a default directory for view composers. You are free to organize them however you wish. For example, you could create an `app/Http/View/Composers` directory: +We'll use the `View` facade's `composer` method to register the view composer. Laravel does not include a default directory for class based view composers, so you are free to organize them however you wish. For example, you could create an `app/Http/View/Composers` directory to house all of your application's view composers: {note} Remember, if you create a new service provider to contain your view composer registrations, you will need to add the service provider to the `providers` array in the `config/app.php` configuration file. -Now that we have registered the composer, the `ProfileComposer@compose` method will be executed each time the `profile` view is being rendered. So, let's define the composer class: +Now that we have registered the composer, the `compose` method of the `App\Http\View\Composers\ProfileComposer` class will be executed each time the `profile` view is being rendered. Let's take a look at an example of the composer class: {tip} All view composers are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a composer's constructor. +As you can see, all view composers are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a composer's constructor. #### Attaching A Composer To Multiple Views You may attach a view composer to multiple views at once by passing an array of views as the first argument to the `composer` method: + use App\Http\Views\Composers\MultiComposer; + View::composer( ['profile', 'dashboard'], - 'App\Http\View\Composers\MyViewComposer' + MultiComposer::class ); The `composer` method also accepts the `*` character as a wildcard, allowing you to attach a composer to all views: @@ -214,18 +240,21 @@ The `composer` method also accepts the `*` character as a wildcard, allowing you }); -#### View Creators +### View Creators -View **creators** are very similar to view composers; however, they are executed immediately after the view is instantiated instead of waiting until the view is about to render. To register a view creator, use the `creator` method: +View "creators" are very similar to view composers; however, they are executed immediately after the view is instantiated instead of waiting until the view is about to render. To register a view creator, use the `creator` method: + + use App\Http\View\Creators\ProfileCreator; + use Illuminate\Support\Facades\View; - View::creator('profile', 'App\Http\View\Creators\ProfileCreator'); + View::creator('profile', ProfileCreator::class); ## Optimizing Views -By default, views are compiled on demand. When a request is executed that renders a view, Laravel will determine if a compiled version of the view exists. If the file exists, Laravel will then determine if the uncompiled view has been modified more recently than the compiled view. If the compiled view either does not exist, or the uncompiled view has been modified, Laravel will recompile the view. +By default, Blade template views are compiled on demand. When a request is executed that renders a view, Laravel will determine if a compiled version of the view exists. If the file exists, Laravel will then determine if the uncompiled view has been modified more recently than the compiled view. If the compiled view either does not exist, or the uncompiled view has been modified, Laravel will recompile the view. -Compiling views during the request negatively impacts performance, so Laravel provides the `view:cache` Artisan command to precompile all of the views utilized by your application. For increased performance, you may wish to run this command as part of your deployment process: +Compiling views during the request may have a small negative impact on performance, so Laravel provides the `view:cache` Artisan command to precompile all of the views utilized by your application. For increased performance, you may wish to run this command as part of your deployment process: php artisan view:cache From 584ac6ae93592c281a46d273775f36feeff0ff0c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 13 Nov 2020 15:36:29 -0600 Subject: [PATCH 064/274] wip --- views.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/views.md b/views.md index 272c457eb1..0466ada7c5 100644 --- a/views.md +++ b/views.md @@ -2,7 +2,7 @@ - [Introduction](#introduction) - [Creating & Rendering Views](#creating-and-rendering-views) - - [Nested Views](#nested-views) + - [Nested View Directories](#nested-view-directories) - [Creating The First Available View](#creating-the-first-available-view) - [Determining If A View Exists](#determining-if-a-view-exists) - [Passing Data To Views](#passing-data-to-views) @@ -51,8 +51,8 @@ Views may also be returned using the `View` facade: 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). - -### Nested Views + +### Nested View Directories Views may also be nested within subdirectories 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 return it from one of your application's routes / controllers like so: From 168d9e10d7e21db87e19ededf9bf60a6a266cb12 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 13 Nov 2020 15:44:39 -0600 Subject: [PATCH 065/274] wip --- documentation.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/documentation.md b/documentation.md index 7a983b6479..9f36139bd8 100644 --- a/documentation.md +++ b/documentation.md @@ -21,37 +21,36 @@ - [Requests](/docs/{{version}}/requests) - [Responses](/docs/{{version}}/responses) - [Views](/docs/{{version}}/views) + - [Blade Templates](/docs/{{version}}/blade) - [URL Generation](/docs/{{version}}/urls) - [Session](/docs/{{version}}/session) - [Validation](/docs/{{version}}/validation) - [Error Handling](/docs/{{version}}/errors) - [Logging](/docs/{{version}}/logging) -- ## Frontend - - [Blade Templates](/docs/{{version}}/blade) - - [Localization](/docs/{{version}}/localization) - - [Compiling Assets](/docs/{{version}}/mix) -- ## Security - - [Authentication](/docs/{{version}}/authentication) - - [Authorization](/docs/{{version}}/authorization) - - [Email Verification](/docs/{{version}}/verification) - - [Encryption](/docs/{{version}}/encryption) - - [Hashing](/docs/{{version}}/hashing) - - [Password Reset](/docs/{{version}}/passwords) - ## Digging Deeper - [Artisan Console](/docs/{{version}}/artisan) - [Broadcasting](/docs/{{version}}/broadcasting) - [Cache](/docs/{{version}}/cache) - [Collections](/docs/{{version}}/collections) + - [Compiling Assets](/docs/{{version}}/mix) - [Contracts](/docs/{{version}}/contracts) - [Events](/docs/{{version}}/events) - [File Storage](/docs/{{version}}/filesystem) - [Helpers](/docs/{{version}}/helpers) - [HTTP Client](/docs/{{version}}/http-client) + - [Localization](/docs/{{version}}/localization) - [Mail](/docs/{{version}}/mail) - [Notifications](/docs/{{version}}/notifications) - [Package Development](/docs/{{version}}/packages) - [Queues](/docs/{{version}}/queues) - [Task Scheduling](/docs/{{version}}/scheduling) +- ## Security + - [Authentication](/docs/{{version}}/authentication) + - [Authorization](/docs/{{version}}/authorization) + - [Email Verification](/docs/{{version}}/verification) + - [Encryption](/docs/{{version}}/encryption) + - [Hashing](/docs/{{version}}/hashing) + - [Password Reset](/docs/{{version}}/passwords) - ## Database - [Getting Started](/docs/{{version}}/database) - [Query Builder](/docs/{{version}}/queries) From 81c4f43b91fdafc646c2c31b5c8b5f670002fd56 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 13 Nov 2020 15:54:53 -0600 Subject: [PATCH 066/274] wip --- blade.md | 24 +++++++++++++++++++++++- installation.md | 2 ++ views.md | 4 ++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/blade.md b/blade.md index 4cd46788ff..8a7d132fbf 100644 --- a/blade.md +++ b/blade.md @@ -26,6 +26,7 @@ - [Inline Component Views](#inline-component-views) - [Anonymous Components](#anonymous-components) - [Dynamic Components](#dynamic-components) + - [Components As Layouts](#components-as-layouts) - [Including Subviews](#including-subviews) - [Rendering Views For Collections](#rendering-views-for-collections) - [Stacks](#stacks) @@ -36,7 +37,9 @@ ## Introduction -Blade is the simple, yet powerful templating engine provided with Laravel. Unlike other popular PHP templating engines, Blade does not restrict you from using plain PHP code in your views. In fact, all Blade views are compiled into plain PHP code and cached until they are modified, meaning Blade adds essentially zero overhead to your application. Blade view files use the `.blade.php` file extension and are typically stored in the `resources/views` directory. +Blade is the simple, yet powerful templating engine that is included with Laravel. Unlike some PHP templating engines, Blade does not restrict you from using plain PHP code in your templates. In fact, all Blade templates are compiled into plain PHP code and cached until they are modified, meaning Blade adds essentially zero overhead to your application. Blade template files use the `.blade.php` file extension and are typically stored in the `resources/views` directory. + +> {tip} Before digging deeper into Blade, make sure to read the Laravel [view documentation](/docs/{{version}}/views). ## Template Inheritance @@ -892,6 +895,25 @@ Sometimes you may need to render a component but not know which component should + +### Components As Layouts + +We previously discussed [template inheritance](#template-inheritance) in this documentation. However, if you prefer, you may use components to achieve the same goals as template inheritance. For example, imagine a "layout" component that looks like the following: + + + +

Application

+
+ {{ $slot }} + + + +Once the `layout` component has been defined, we may create a Blade component that utilizes the `layout` component to achieve the same goals as template inheritance: + + + Page content... + + ## Including Subviews diff --git a/installation.md b/installation.md index b4ebdab8b9..731f2da1a7 100644 --- a/installation.md +++ b/installation.md @@ -369,6 +369,8 @@ Laravel may serve as a full stack framework. By "full stack" framework we mean t If this is how you plan to use Laravel, you may want to check out our documentation on [routing](/docs/{{version}}/routing), [views](/docs/{{version}}/views), or the [Eloquent ORM](/docs/{{version}}/eloquent). In addition, you might be interested in learning about community packages like [Livewire](https://laravel-livewire.com) and [Inertia.js](https://inertiajs.com). These packages allow you to use Laravel as a full-stack framework while enjoying many of the UI benefits provided by single-page JavaScript applications. +If you are using Laravel as a full stack framework, we also strongly encourage you to learn how to compile your application's CSS and JavaScript using [Laravel Mix](/docs/{{version}}/mix). + > {tip} If you want to get a head start building your application, check out one of our official [application starter kits](/docs/{{version}}/starter-kits). diff --git a/views.md b/views.md index 0466ada7c5..bfe0bce8a9 100644 --- a/views.md +++ b/views.md @@ -14,8 +14,6 @@ ## Introduction -> {tip} Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started. - Of course, it's not practical to return entire HTML documents strings directly from your routes and controllers. Thankfully, views provide a convenient way to place all of our HTML in separate files. Views separate your controller / application logic from your presentation logic and are stored in the `resources/views` directory. A simple view might look something like this: @@ -32,6 +30,8 @@ Since this view is stored at `resources/views/greeting.blade.php`, we may return return view('greeting', ['name' => 'James']); }); +> {tip} Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started. + ## Creating & Rendering Views From 4810cfb0ac8d5976c502f79386f16444a32191c4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 13 Nov 2020 16:13:19 -0600 Subject: [PATCH 067/274] wip --- blade.md | 114 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 55 deletions(-) diff --git a/blade.md b/blade.md index 8a7d132fbf..a888eaa8a2 100644 --- a/blade.md +++ b/blade.md @@ -5,6 +5,7 @@ - [Defining A Layout](#defining-a-layout) - [Extending A Layout](#extending-a-layout) - [Displaying Data](#displaying-data) + - [HTML Entity Encoding](#html-entity-encoding) - [Blade & JavaScript Frameworks](#blade-and-javascript-frameworks) - [Control Structures](#control-structures) - [If Statements](#if-statements) @@ -19,7 +20,7 @@ - [Method Field](#method-field) - [Validation Errors](#validation-errors) - [Components](#components) - - [Displaying Components](#displaying-components) + - [Rendering Components](#rendering-components) - [Passing Data To Components](#passing-data-to-components) - [Managing Attributes](#managing-attributes) - [Slots](#slots) @@ -39,6 +40,12 @@ Blade is the simple, yet powerful templating engine that is included with Laravel. Unlike some PHP templating engines, Blade does not restrict you from using plain PHP code in your templates. In fact, all Blade templates are compiled into plain PHP code and cached until they are modified, meaning Blade adds essentially zero overhead to your application. Blade template files use the `.blade.php` file extension and are typically stored in the `resources/views` directory. +Blade views may be returned from routes or controller using the global `view` helper: + + Route::get('/', function () { + return view('greeting'); + }); + > {tip} Before digging deeper into Blade, make sure to read the Laravel [view documentation](/docs/{{version}}/views). @@ -47,7 +54,7 @@ Blade is the simple, yet powerful templating engine that is included with Larave ### Defining A Layout -Two of the primary benefits of using Blade are _template inheritance_ and _sections_. To get started, let's take a look at a simple example. First, we will examine a "master" page layout. Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Blade view: +Two of the primary benefits of using Blade are _template inheritance_ and _sections_. To get started, let's take a look at a simple example. First, we will examine a page layout. Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Blade view: @@ -73,7 +80,7 @@ Now that we have defined a layout for our application, let's define a child page ### Extending A Layout -When defining a child view, use the Blade `@extends` directive to specify which layout the child view should "inherit". Views which extend a Blade layout may inject content into the layout's sections using `@section` directives. Remember, as seen in the example above, the contents of these sections will be displayed in the layout using `@yield`: +When defining a child view, use the `@extends` Blade directive to specify which layout the child view should "inherit". Views which extend a Blade layout may inject content into the layout's sections using `@section` directives. Remember, as seen in the example above, the contents of these sections will be displayed in the layout using `@yield`: @@ -97,20 +104,14 @@ In this example, the `sidebar` section is utilizing the `@@parent` directive to The `@yield` directive also accepts a default value as its second parameter. This value will be rendered if the section being yielded is undefined: - @yield('content', View::make('view.name')) - -Blade views may be returned from routes using the global `view` helper: - - Route::get('blade', function () { - return view('child'); - }); + @yield('content', 'Default content') ## Displaying Data -You may display data passed to your Blade views by wrapping the variable in curly braces. For example, given the following route: +You may display data that is passed to your Blade views by wrapping the variable in curly braces. For example, given the following route: - Route::get('greeting', function () { + Route::get('/', function () { return view('welcome', ['name' => 'Samantha']); }); @@ -118,21 +119,12 @@ You may display the contents of the `name` variable like so: Hello, {{ $name }}. -> {tip} Blade `{{ }}` statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks. +> {tip} Blade's `{{ }}` echo statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks. You are not limited to displaying the contents of the variables passed to the view. You may also echo the results of any PHP function. In fact, you can put any PHP code you wish inside of a Blade echo statement: The current UNIX timestamp is {{ time() }}. - -#### Displaying Unescaped Data - -By default, Blade `{{ }}` statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks. If you do not want your data to be escaped, you may use the following syntax: - - Hello, {!! $name !!}. - -> {note} Be very careful when echoing content that is supplied by users of your application. Always use the escaped, double curly brace syntax to prevent XSS attacks when displaying user supplied data. - #### Rendering JSON @@ -153,7 +145,7 @@ However, instead of manually calling `json_encode`, you may use the `@json` Blad > {note} You should only use the `@json` directive to render existing variables as JSON. The Blade templating is based on regular expressions and attempts to pass a complex expression to the directive may cause unexpected failures. -#### HTML Entity Encoding +### HTML Entity Encoding By default, Blade (and the Laravel `e` helper) will double encode HTML entities. If you would like to disable double encoding, call the `Blade::withoutDoubleEncoding` method from the `boot` method of your `AppServiceProvider`: @@ -177,6 +169,15 @@ By default, Blade (and the Laravel `e` helper) will double encode HTML entities. } } + +#### Displaying Unescaped Data + +By default, Blade `{{ }}` statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks. If you do not want your data to be escaped, you may use the following syntax: + + Hello, {!! $name !!}. + +> {note} Be very careful when echoing content that is supplied by users of your application. You should typically use the escaped, double curly brace syntax to prevent XSS attacks when displaying user supplied data. + ### Blade & JavaScript Frameworks @@ -186,11 +187,11 @@ Since many JavaScript frameworks also use "curly" braces to indicate a given exp Hello, @{{ name }}. -In this example, the `@` symbol will be removed by Blade; however, `{{ name }}` expression will remain untouched by the Blade engine, allowing it to instead be rendered by your JavaScript framework. +In this example, the `@` symbol will be removed by Blade; however, `{{ name }}` expression will remain untouched by the Blade engine, allowing it to be rendered by your JavaScript framework. The `@` symbol may also be used to escape Blade directives: - {{-- Blade --}} + {{-- Blade template --}} @@json() @@ -210,7 +211,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 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. +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 @@ -244,7 +245,7 @@ In addition to the conditional directives already discussed, the `@isset` and `@ #### Authentication Directives -The `@auth` and `@guest` directives may be used to quickly determine if the current user is authenticated or is a guest: +The `@auth` and `@guest` directives may be used to quickly determine if the current user is [authenticated](/docs/{{version}}/authentication) or is a guest: @auth // The user is authenticated... @@ -254,7 +255,7 @@ The `@auth` and `@guest` directives may be used to quickly determine if the curr // The user is not authenticated... @endguest -If needed, you may specify the [authentication guard](/docs/{{version}}/authentication) that should be checked when using the `@auth` and `@guest` directives: +If needed, you may specify the authentication guard that should be checked when using the `@auth` and `@guest` directives: @auth('admin') // The user is authenticated... @@ -264,10 +265,29 @@ If needed, you may specify the [authentication guard](/docs/{{version}}/authenti // The user is not authenticated... @endguest + +#### Environment Directives + +You may check if the application is running in the production environment using the `@production` directive: + + @production + // Production specific content... + @endproduction + +Or, you may determine if the application is running in a specific environment using the `@env` directive: + + @env('staging') + // The application is running in "staging"... + @endenv + + @env(['staging', 'production']) + // The application is running in "staging" or "production"... + @endenv + #### Section Directives -You may check if a section has content using the `@hasSection` directive: +You may determine if a template inheritance section has content using the `@hasSection` directive: @hasSection('navigation')
@@ -285,25 +305,6 @@ You may use the `sectionMissing` directive to determine if a section does not ha
@endif - -#### Environment Directives - -You may check if the application is running in the production environment using the `@production` directive: - - @production - // Production specific content... - @endproduction - -Or, you may determine if the application is running in a specific environment using the `@env` directive: - - @env('staging') - // The application is running in "staging"... - @endenv - - @env(['staging', 'production']) - // The application is running in "staging" or "production"... - @endenv - ### Switch Statements @@ -347,7 +348,7 @@ In addition to conditional statements, Blade provides simple directives for work > {tip} When looping, you may use the [loop variable](#the-loop-variable) to gain valuable information about the loop, such as whether you are in the first or last iteration through the loop. -When using loops you may also end the loop or skip the current iteration: +When using loops you may also end the loop or skip the current iteration using the `@continue` and `@break` directives: @foreach ($users as $user) @if ($user->type == 1) @@ -361,7 +362,7 @@ When using loops you may also end the loop or skip the current iteration: @endif @endforeach -You may also include the condition with the directive declaration in one line: +You may also include the continuation or break condition within the directive declaration: @foreach ($users as $user) @continue($user->type == 1) @@ -426,11 +427,9 @@ Blade also allows you to define comments in your views. However, unlike HTML com 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 - // + $counter = 1; @endphp -> {tip} While Blade provides this feature, using it frequently may be a signal that you have too much logic embedded within your template. - ### The `@once` Directive @@ -510,11 +509,14 @@ The `make:component` command will also create a view template for the component. #### Manually Registering Package Components +> {note} The following documentation on manually registering components is primarily applicable to those who are writing Laravel packages that include view components. If you are not writing a package, this portion of the component documentation may not be relevant to you. + When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. However, if you are building a package that utilizes Blade components, you will need to manually register your component class and its HTML tag alias. You should typically register your components in the `boot` method of your package's service provider: use Illuminate\Support\Facades\Blade; + use VendorPackage\View\Components\AlertComponent; /** * Bootstrap your package's services. @@ -528,6 +530,8 @@ Once your component has been registered, it may be rendered using its tag alias: +**Autoloading Package Components** + Alternatively, you may use the `componentNamespace` method to autoload component classes by convention. For example, a `Nightshade` package might have `Calendar` and `ColorPicker` components that reside within the `Package\Views\Components` namespace: use Illuminate\Support\Facades\Blade; @@ -547,8 +551,8 @@ This will allow the usage of package components by their vendor namespace using Blade will automatically detect the class that's linked to this component by pascal-casing the component name. Subdirectories are also supported using "dot" notation. - -### Displaying Components + +### Rendering Components To display a component, you may use a Blade component tag within one of your Blade templates. Blade component tags start with the string `x-` followed by the kebab case name of the component class: From 33ad4f8212f429f84f655503da37b88135343b59 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 14 Nov 2020 09:21:12 -0600 Subject: [PATCH 068/274] wip --- blade.md | 97 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/blade.md b/blade.md index a888eaa8a2..f800a6a294 100644 --- a/blade.md +++ b/blade.md @@ -28,6 +28,7 @@ - [Anonymous Components](#anonymous-components) - [Dynamic Components](#dynamic-components) - [Components As Layouts](#components-as-layouts) + - [Manually Registering Components](#manually-registering-components) - [Including Subviews](#including-subviews) - [Rendering Views For Collections](#rendering-views-for-collections) - [Stacks](#stacks) @@ -504,52 +505,7 @@ To create a class based component, you may use the `make:component` Artisan comm php artisan make:component Alert -The `make:component` command will also create a view template for the component. The view will be placed in the `resources/views/components` directory. - - -#### Manually Registering Package Components - -> {note} The following documentation on manually registering components is primarily applicable to those who are writing Laravel packages that include view components. If you are not writing a package, this portion of the component documentation may not be relevant to you. - -When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. - -However, if you are building a package that utilizes Blade components, you will need to manually register your component class and its HTML tag alias. You should typically register your components in the `boot` method of your package's service provider: - - use Illuminate\Support\Facades\Blade; - use VendorPackage\View\Components\AlertComponent; - - /** - * Bootstrap your package's services. - */ - public function boot() - { - Blade::component('package-alert', AlertComponent::class); - } - -Once your component has been registered, it may be rendered using its tag alias: - - - -**Autoloading Package Components** - -Alternatively, you may use the `componentNamespace` method to autoload component classes by convention. For example, a `Nightshade` package might have `Calendar` and `ColorPicker` components that reside within the `Package\Views\Components` namespace: - - use Illuminate\Support\Facades\Blade; - - /** - * Bootstrap your package's services. - */ - public function boot() - { - Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); - } - -This will allow the usage of package components by their vendor namespace using the `package-name::` syntax: - - - - -Blade will automatically detect the class that's linked to this component by pascal-casing the component name. Subdirectories are also supported using "dot" notation. +The `make:component` command will also create a view template for the component. The view will be placed in the `resources/views/components` directory. When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. ### Rendering Components @@ -918,6 +874,55 @@ Once the `layout` component has been defined, we may create a Blade component th Page content... + +### Manually Registering Components + +> {note} The following documentation on manually registering components is primarily applicable to those who are writing Laravel packages that include view components. If you are not writing a package, this portion of the component documentation may not be relevant to you. + +When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. + +However, if you are building a package that utilizes Blade components or placing components in non-conventional directories, you will need to manually register your component class and its HTML tag alias. You should typically register your components in the `boot` method of your package's service provider: + + use Illuminate\Support\Facades\Blade; + use VendorPackage\View\Components\AlertComponent; + + /** + * Bootstrap your package's services. + * + * @return void + */ + public function boot() + { + Blade::component('package-alert', AlertComponent::class); + } + +Once your component has been registered, it may be rendered using its tag alias: + + + +#### Autoloading Package Components + +Alternatively, you may use the `componentNamespace` method to autoload component classes by convention. For example, a `Nightshade` package might have `Calendar` and `ColorPicker` components that reside within the `Package\Views\Components` namespace: + + use Illuminate\Support\Facades\Blade; + + /** + * Bootstrap your package's services. + * + * @return void + */ + public function boot() + { + Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade'); + } + +This will allow the usage of package components by their vendor namespace using the `package-name::` syntax: + + + + +Blade will automatically detect the class that's linked to this component by pascal-casing the component name. Subdirectories are also supported using "dot" notation. + ## Including Subviews From 513d73e498950934e9f34dec164cdf9a5e2768da Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 14 Nov 2020 09:23:38 -0600 Subject: [PATCH 069/274] wip --- blade.md | 121 ++++++++++++++++++++++++------------------------------- 1 file changed, 52 insertions(+), 69 deletions(-) diff --git a/blade.md b/blade.md index f800a6a294..9cfdd61514 100644 --- a/blade.md +++ b/blade.md @@ -19,6 +19,8 @@ - [CSRF Field](#csrf-field) - [Method Field](#method-field) - [Validation Errors](#validation-errors) +- [Including Subviews](#including-subviews) + - [Rendering Views For Collections](#rendering-views-for-collections) - [Components](#components) - [Rendering Components](#rendering-components) - [Passing Data To Components](#passing-data-to-components) @@ -29,8 +31,6 @@ - [Dynamic Components](#dynamic-components) - [Components As Layouts](#components-as-layouts) - [Manually Registering Components](#manually-registering-components) -- [Including Subviews](#including-subviews) - - [Rendering Views For Collections](#rendering-views-for-collections) - [Stacks](#stacks) - [Service Injection](#service-injection) - [Extending Blade](#extending-blade) @@ -496,6 +496,56 @@ You may pass [the name of a specific error bag](/docs/{{version}}/validation#nam
{{ $message }}
@enderror + +## Including Subviews + +Blade's `@include` directive allows you to include a Blade view from within another view. All variables that are available to the parent view will be made available to the included view: + +
+ @include('shared.errors') + +
+ +
+
+ +Even though the included view will inherit all data available in the parent view, you may also pass an array of extra data to the included view: + + @include('view.name', ['some' => 'data']) + +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']) + +If you would like to `@include` a view if a given boolean expression evaluates to `true`, you may use the `@includeWhen` directive: + + @includeWhen($boolean, 'view.name', ['some' => 'data']) + +If you would like to `@include` a view if a given boolean expression evaluates to `false`, you may use the `@includeUnless` directive: + + @includeUnless($boolean, 'view.name', ['some' => 'data']) + +To include the first view that exists from a given array of views, you may use the `includeFirst` directive: + + @includeFirst(['custom.admin', 'admin'], ['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. + + +### Rendering Views For Collections + +You may combine loops and includes into one line with Blade's `@each` directive: + + @each('view.name', $jobs, 'job') + +The first argument is the view partial to render for each element in the array or collection. The second argument is the array or collection you wish to iterate over, while the third argument is the variable name that will be assigned to the current iteration within the view. So, for example, if you are iterating over an array of `jobs`, typically you will want to access each job as a `job` variable within your view partial. The key for the current iteration will be available as the `key` variable within your view partial. + +You may also pass a fourth argument to the `@each` directive. This argument determines the view that will be rendered if the given array is empty. + + @each('view.name', $jobs, 'job', 'view.empty') + +> {note} Views rendered via `@each` do not inherit the variables from the parent view. If the child view requires these variables, you should use `@foreach` and `@include` instead. + ## Components @@ -923,73 +973,6 @@ This will allow the usage of package components by their vendor namespace using Blade will automatically detect the class that's linked to this component by pascal-casing the component name. Subdirectories are also supported using "dot" notation. - -## Including Subviews - -Blade's `@include` directive allows you to include a Blade view from within another view. All variables that are available to the parent view will be made available to the included view: - -
- @include('shared.errors') - -
- -
-
- -Even though the included view will inherit all data available in the parent view, you may also pass an array of extra data to the included view: - - @include('view.name', ['some' => 'data']) - -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']) - -If you would like to `@include` a view if a given boolean expression evaluates to `true`, you may use the `@includeWhen` directive: - - @includeWhen($boolean, 'view.name', ['some' => 'data']) - -If you would like to `@include` a view if a given boolean expression evaluates to `false`, you may use the `@includeUnless` directive: - - @includeUnless($boolean, 'view.name', ['some' => 'data']) - -To include the first view that exists from a given array of views, you may use the `includeFirst` directive: - - @includeFirst(['custom.admin', 'admin'], ['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. - - -#### Aliasing Includes - -If your Blade includes are stored in a subdirectory, you may wish to alias them for easier access. For example, imagine a Blade include that is stored at `resources/views/includes/input.blade.php` with the following content: - - - -You may use the `include` method to alias the include from `includes.input` to `input`. Typically, this should be done in the `boot` method of your `AppServiceProvider`: - - use Illuminate\Support\Facades\Blade; - - Blade::include('includes.input', 'input'); - -Once the include has been aliased, you may render it using the alias name as the Blade directive: - - @input(['type' => 'email']) - - -### Rendering Views For Collections - -You may combine loops and includes into one line with Blade's `@each` directive: - - @each('view.name', $jobs, 'job') - -The first argument is the view partial to render for each element in the array or collection. The second argument is the array or collection you wish to iterate over, while the third argument is the variable name that will be assigned to the current iteration within the view. So, for example, if you are iterating over an array of `jobs`, typically you will want to access each job as a `job` variable within your view partial. The key for the current iteration will be available as the `key` variable within your view partial. - -You may also pass a fourth argument to the `@each` directive. This argument determines the view that will be rendered if the given array is empty. - - @each('view.name', $jobs, 'job', 'view.empty') - -> {note} Views rendered via `@each` do not inherit the variables from the parent view. If the child view requires these variables, you should use `@foreach` and `@include` instead. - ## Stacks From 27432ad8fd552ae41815c960d27b5c9c05dbcdda Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 14 Nov 2020 09:46:38 -0600 Subject: [PATCH 070/274] wip --- blade.md | 104 +++++++++++++++++++++++++++---------------------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/blade.md b/blade.md index 9cfdd61514..a03d0c463d 100644 --- a/blade.md +++ b/blade.md @@ -15,12 +15,12 @@ - [Comments](#comments) - [PHP](#php) - [The `@once` Directive](#the-once-directive) +- [Including Subviews](#including-subviews) + - [Rendering Views For Collections](#rendering-views-for-collections) - [Forms](#forms) - [CSRF Field](#csrf-field) - [Method Field](#method-field) - [Validation Errors](#validation-errors) -- [Including Subviews](#including-subviews) - - [Rendering Views For Collections](#rendering-views-for-collections) - [Components](#components) - [Rendering Components](#rendering-components) - [Passing Data To Components](#passing-data-to-components) @@ -444,6 +444,56 @@ The `@once` directive allows you to define a portion of the template that will o @endpush @endonce + +## Including Subviews + +> {tip} While you're free to use the `@include` directive, Blade [components](#components) provide similar functionality and offer several benefits over the `@include` directive such as data and attribute binding. + +Blade's `@include` directive allows you to include a Blade view from within another view. All variables that are available to the parent view will be made available to the included view: + +
+ @include('shared.errors') + +
+ +
+
+ +Even though the included view will inherit all data available in the parent view, you may also pass an array of additional data that should be made available to the included view: + + @include('view.name', ['status' => 'complete']) + +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', ['status' => 'complete']) + +If you would like to `@include` a view if a given boolean expression evaluates to `true` or `false`, you may use the `@includeWhen` and `@includeUnless` directives: + + @includeWhen($boolean, 'view.name', ['status' => 'complete']) + + @includeUnless($boolean, 'view.name', ['status' => 'complete']) + +To include the first view that exists from a given array of views, you may use the `includeFirst` directive: + + @includeFirst(['custom.admin', 'admin'], ['status' => 'complete']) + +> {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. + + +### Rendering Views For Collections + +You may combine loops and includes into one line with Blade's `@each` directive: + + @each('view.name', $jobs, 'job') + +The `@each` directive's first argument is the view to render for each element in the array or collection. The second argument is the array or collection you wish to iterate over, while the third argument is the variable name that will be assigned to the current iteration within the view. So, for example, if you are iterating over an array of `jobs`, typically you will want to access each job as a `job` variable within the view. The array key for the current iteration will be available as the `key` variable within the view. + +You may also pass a fourth argument to the `@each` directive. This argument determines the view that will be rendered if the given array is empty. + + @each('view.name', $jobs, 'job', 'view.empty') + +> {note} Views rendered via `@each` do not inherit the variables from the parent view. If the child view requires these variables, you should use the `@foreach` and `@include` directives instead. + ## Forms @@ -496,56 +546,6 @@ You may pass [the name of a specific error bag](/docs/{{version}}/validation#nam
{{ $message }}
@enderror - -## Including Subviews - -Blade's `@include` directive allows you to include a Blade view from within another view. All variables that are available to the parent view will be made available to the included view: - -
- @include('shared.errors') - -
- -
-
- -Even though the included view will inherit all data available in the parent view, you may also pass an array of extra data to the included view: - - @include('view.name', ['some' => 'data']) - -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']) - -If you would like to `@include` a view if a given boolean expression evaluates to `true`, you may use the `@includeWhen` directive: - - @includeWhen($boolean, 'view.name', ['some' => 'data']) - -If you would like to `@include` a view if a given boolean expression evaluates to `false`, you may use the `@includeUnless` directive: - - @includeUnless($boolean, 'view.name', ['some' => 'data']) - -To include the first view that exists from a given array of views, you may use the `includeFirst` directive: - - @includeFirst(['custom.admin', 'admin'], ['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. - - -### Rendering Views For Collections - -You may combine loops and includes into one line with Blade's `@each` directive: - - @each('view.name', $jobs, 'job') - -The first argument is the view partial to render for each element in the array or collection. The second argument is the array or collection you wish to iterate over, while the third argument is the variable name that will be assigned to the current iteration within the view. So, for example, if you are iterating over an array of `jobs`, typically you will want to access each job as a `job` variable within your view partial. The key for the current iteration will be available as the `key` variable within your view partial. - -You may also pass a fourth argument to the `@each` directive. This argument determines the view that will be rendered if the given array is empty. - - @each('view.name', $jobs, 'job', 'view.empty') - -> {note} Views rendered via `@each` do not inherit the variables from the parent view. If the child view requires these variables, you should use `@foreach` and `@include` instead. - ## Components From b2e204fadbacf8cec12517a7181bdf1dfaea50db Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 14 Nov 2020 10:29:00 -0600 Subject: [PATCH 071/274] wip --- blade.md | 76 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/blade.md b/blade.md index a03d0c463d..f37993f855 100644 --- a/blade.md +++ b/blade.md @@ -24,7 +24,7 @@ - [Components](#components) - [Rendering Components](#rendering-components) - [Passing Data To Components](#passing-data-to-components) - - [Managing Attributes](#managing-attributes) + - [Component Attributes](#component-attributes) - [Slots](#slots) - [Inline Component Views](#inline-component-views) - [Anonymous Components](#anonymous-components) @@ -549,13 +549,13 @@ You may pass [the name of a specific error bag](/docs/{{version}}/validation#nam ## Components -Components and slots provide similar benefits to sections and layouts; however, some may find the mental model of components and slots easier to understand. There are two approaches to writing components: class based components and anonymous components. +Components and slots provide similar benefits to sections, layouts, and includes; however, some may find the mental model of components and slots easier to understand. There are two approaches to writing components: class based components and anonymous components. To create a class based component, you may use the `make:component` Artisan command. To illustrate how to use components, we will create a simple `Alert` component. The `make:component` command will place the component in the `App\View\Components` directory: php artisan make:component Alert -The `make:component` command will also create a view template for the component. The view will be placed in the `resources/views/components` directory. When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. +The `make:component` command will also create a view template for the component. The view will be placed in the `resources/views/components` directory. When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory, so no further component registration is typically required. ### Rendering Components @@ -573,7 +573,7 @@ If the component class is nested deeper within the `App\View\Components` directo ### Passing Data To Components -You may pass data to Blade components using HTML attributes. Hard-coded, primitive values may be passed to the component using simple HTML attributes. PHP expressions and variables should be passed to the component via attributes that use the `:` character as a prefix: +You may pass data to Blade components using HTML attributes. Hard-coded, primitive values may be passed to the component using simple HTML attribute strings. PHP expressions and variables should be passed to the component via attributes that use the `:` character as a prefix: @@ -647,14 +647,14 @@ Component constructor arguments should be specified using `camelCase`, while `ke $this->alertType = $alertType; } -The `$alertType` argument may be provided like so: +The `$alertType` argument may be provided to the component like so: #### Component Methods -In addition to public variables being available to your component template, any public methods on the component may also be executed. For example, imagine a component that has a `isSelected` method: +In addition to public variables being available to your component template, any public methods on the component may be invoked. For example, imagine a component that has a `isSelected` method: /** * Determine if the given option is the current selected option. @@ -673,10 +673,10 @@ You may execute this method from your component template by invoking the variabl {{ $label }} - -#### Using Attributes & Slots Inside The Class + +#### Accessing Attributes & Slots Within Component Classes -Blade components also allow you to access the component name, attributes, and slot inside the class's render method. However, in order to access this data, you should return a closure from your component's `render` method. The closure will receive a `$data` array as its only argument: +Blade components also allow you to access the component name, attributes, and slot inside the class's render method. However, in order to access this data, you should return a closure from your component's `render` method. The closure will receive a `$data` array as its only argument. This array will contain several elements that provide information about the component: /** * Get the view / contents that represent the component. @@ -694,7 +694,7 @@ Blade components also allow you to access the component name, attributes, and sl }; } -The `componentName` is equal to the name used in the HTML tag after the `x-` prefix. So ``'s `componentName` will be `alert`. The `attributes` element will contain all of the attributes that were present on the HTML tag. The `slot` element is an `Illuminate\Support\HtmlString` instance with the contents of the slot from the component. +The `componentName` is equal to the name used in the HTML tag after the `x-` prefix. So ``'s `componentName` will be `alert`. The `attributes` element will contain all of the attributes that were present on the HTML tag. The `slot` element is an `Illuminate\Support\HtmlString` instance with the contents of the component's slot. The closure should return a string. If the returned string corresponds to an existing view, that view will be rendered; otherwise, the returned string will be evaluated as an inline Blade view. @@ -720,8 +720,8 @@ If your component requires dependencies from Laravel's [service container](/docs $this->message = $message; } - -### Managing Attributes + +### Component Attributes We've already examined how to pass data attributes to a component; however, sometimes you may need to specify additional HTML attributes, such as `class`, that are not part of the data required for a component to function. Typically, you want to pass these additional attributes down to the root element of the component template. For example, imagine we want to render an `alert` component like so: @@ -730,15 +730,15 @@ We've already examined how to pass data attributes to a component; however, some All of the attributes that are not part of the component's constructor will automatically be added to the component's "attribute bag". This attribute bag is automatically made available to the component via the `$attributes` variable. All of the attributes may be rendered within the component by echoing this variable:
- +
-> {note} Using directives such as `@env` directly on a component is not supported at this time. +> {note} Using directives such as `@env` within component tags is not supported at this time. For example, `` will not be compiled. #### Default / Merged Attributes -Sometimes you may need to specify default values for attributes or merge additional values into some of the component's attributes. To accomplish this, you may use the attribute bag's `merge` method: +Sometimes you may need to specify default values for attributes or merge additional values into some of the component's attributes. To accomplish this, you may use the attribute bag's `merge` method. This method is particularly useful for defining a set a default CSS classes that should always be applied to a component:
merge(['class' => 'alert alert-'.$type]) }}> {{ $message }} @@ -757,7 +757,7 @@ The final, rendered HTML of the component will appear like the following: #### Non-Class Attribute Merging -When merging attributes that are not `class` attributes, the values provided to the `merge` method will be considered the "default" values of attribute which can be overwritten by the component's consumer. Unlike `class` attributes, non-class attributes are not appended to each other. For example, a `button` component may look like the following: +When merging attributes that are not `class` attributes, the values provided to the `merge` method will be considered the "default" values of attribute. However, unlike the `class` attribute, these attributes will not be merged with injected attribute values. Instead, they will be overwritten. For example, a `button` component's implementation may look like the following: -If you would like an attribute other than `class` to have its values appended together, you may use the `prepends` method: +If you would like an attribute other than `class` to have its default value and injected values joined together, you may use the `prepends` method. In this example, the `data-controller` attribute will always begin with `profile-controller` and any additional injected `data-controller` values will be placed after this default value:
merge(['data-controller' => $attributes->prepends('profile-controller')]) }}> {{ $slot }} @@ -799,7 +799,7 @@ Using the `first` method, you may render the first attribute in a given attribut ### Slots -Often, you will need to pass additional content to your component via "slots". Let's imagine that an `alert` component we created has the following markup: +You will often need to pass additional content to your component via "slots". Component slots are rendered by echoing the `$slot` variable. To explore this concept, let's imagine that an `alert` component has the following markup: @@ -813,7 +813,7 @@ We may pass content to the `slot` by injecting content into the component: Whoops! Something went wrong! -Sometimes a component may need to render multiple different slots in different locations within the component. Let's modify our alert component to allow for the injection of a "title": +Sometimes a component may need to render multiple different slots in different locations within the component. Let's modify our alert component to allow for the injection of a "title" slot: @@ -823,7 +823,7 @@ Sometimes a component may need to render multiple different slots in different l {{ $slot }}
-You may define the content of the named slot using the `x-slot` tag. Any content not within an `x-slot` tag will be passed to the component in the `$slot` variable: +You may define the content of the named slot using the `x-slot` tag. Any content not within an explicit `x-slot` tag will be passed to the component in the `$slot` variable: @@ -836,7 +836,7 @@ You may define the content of the named slot using the `x-slot` tag. Any content #### Scoped Slots -If you have used a JavaScript framework such as Vue, you may be familiar with "scoped slots", which allow you to access data or methods from the component within your slot. You may achieve similar behavior in Laravel by defining public methods or properties on your component and accessing the component within your slot via the `$component` variable: +If you have used a JavaScript framework such as Vue, you may be familiar with "scoped slots", which allow you to access data or methods from the component within your slot. You may achieve similar behavior in Laravel by defining public methods or properties on your component and accessing the component within your slot via the `$component` variable. In this example, we will assume that the `x-alert` component has a public `formatAlert` method defined on its component class: @@ -875,7 +875,7 @@ To create a component that renders an inline view, you may use the `inline` opti ### Anonymous Components -Similar to inline components, anonymous components provide a mechanism for managing a component via a single file. However, anonymous components utilize a single view file and have no associated class. To define an anonymous component, you only need to place a Blade template within your `resources/views/components` directory. For example, assuming you have defined a component at `resources/views/components/alert.blade.php`: +Similar to inline components, anonymous components provide a mechanism for managing a component via a single file. However, anonymous components utilize a single view file and have no associated class. To define an anonymous component, you only need to place a Blade template within your `resources/views/components` directory. For example, assuming you have defined a component at `resources/views/components/alert.blade.php`, you may simply render it like so: @@ -898,6 +898,10 @@ You may specify which attributes should be considered data variables using the ` {{ $message }}
+Given the component definition above, we may render the component like so: + + + ### Dynamic Components @@ -908,7 +912,7 @@ Sometimes you may need to render a component but not know which component should ### Components As Layouts -We previously discussed [template inheritance](#template-inheritance) in this documentation. However, if you prefer, you may use components to achieve the same goals as template inheritance. For example, imagine a "layout" component that looks like the following: +We previously discussed [template inheritance](#template-inheritance) in this documentation. However, if you prefer, you may use components to achieve the same goals as template inheritance. For example, imagine a `layout` component that looks like the following: @@ -918,7 +922,7 @@ We previously discussed [template inheritance](#template-inheritance) in this do -Once the `layout` component has been defined, we may create a Blade component that utilizes the `layout` component to achieve the same goals as template inheritance: +Once the `layout` component has been defined, we may create a Blade template that utilizes the `layout` component to achieve the same goals as template inheritance: Page content... @@ -931,7 +935,7 @@ Once the `layout` component has been defined, we may create a Blade component th When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory. -However, if you are building a package that utilizes Blade components or placing components in non-conventional directories, you will need to manually register your component class and its HTML tag alias. You should typically register your components in the `boot` method of your package's service provider: +However, if you are building a package that utilizes Blade components or placing components in non-conventional directories, you will need to manually register your component class and its HTML tag alias so that Laravel knows where to find the component. You should typically register your components in the `boot` method of your package's service provider: use Illuminate\Support\Facades\Blade; use VendorPackage\View\Components\AlertComponent; @@ -1061,7 +1065,7 @@ As you can see, we will chain the `format` method onto whatever expression is pa ### Custom If Statements -Programming a custom directive is sometimes more complex than necessary when defining simple, custom conditional statements. For that reason, Blade provides a `Blade::if` method which allows you to quickly define custom conditional directives using closures. For example, let's define a custom conditional that checks the current application cloud provider. We may do this in the `boot` method of our `AppServiceProvider`: +Programming a custom directive is sometimes more complex than necessary when defining simple, custom conditional statements. For that reason, Blade provides a `Blade::if` method which allows you to quickly define custom conditional directives using closures. For example, let's define a custom conditional that checks the configured default "disk" for the application. We may do this in the `boot` method of our `AppServiceProvider`: use Illuminate\Support\Facades\Blade; @@ -1072,21 +1076,21 @@ Programming a custom directive is sometimes more complex than necessary when def */ public function boot() { - Blade::if('cloud', function ($provider) { - return config('filesystems.default') === $provider; + Blade::if('disk', function ($value) { + return config('filesystems.default') === $value; }); } -Once the custom conditional has been defined, we can easily use it on our templates: +Once the custom conditional has been defined, you can use it within your templates: - @cloud('digitalocean') - // The application is using the digitalocean cloud provider... - @elsecloud('aws') - // The application is using the aws provider... + @disk('local') + // The application is using the local disk... + @elsedisk('s3') + // The application is using the s3 disk... @else - // The application is not using the digitalocean or aws environment... + // The application is using some other disk... @endcloud - @unlesscloud('aws') - // The application is not using the aws environment... + @unlessdisk('local') + // The application is not using the local disk... @endcloud From 40e442de4b3787cb66ffd2f6eb47b0a1410262b7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 14 Nov 2020 10:29:25 -0600 Subject: [PATCH 072/274] fix end statements --- blade.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blade.md b/blade.md index f37993f855..3ed58044c6 100644 --- a/blade.md +++ b/blade.md @@ -1089,8 +1089,8 @@ Once the custom conditional has been defined, you can use it within your templat // The application is using the s3 disk... @else // The application is using some other disk... - @endcloud + @enddisk @unlessdisk('local') // The application is not using the local disk... - @endcloud + @enddisk From d28b027157b4e4f1797dd6371c20fb69068fa90d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 14 Nov 2020 14:18:59 -0600 Subject: [PATCH 073/274] wip --- blade.md | 235 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 118 insertions(+), 117 deletions(-) diff --git a/blade.md b/blade.md index 3ed58044c6..7c7e74c8ae 100644 --- a/blade.md +++ b/blade.md @@ -1,26 +1,18 @@ # Blade Templates - [Introduction](#introduction) -- [Template Inheritance](#template-inheritance) - - [Defining A Layout](#defining-a-layout) - - [Extending A Layout](#extending-a-layout) - [Displaying Data](#displaying-data) - [HTML Entity Encoding](#html-entity-encoding) - [Blade & JavaScript Frameworks](#blade-and-javascript-frameworks) -- [Control Structures](#control-structures) +- [Blade Directives](#blade-directives) - [If Statements](#if-statements) - [Switch Statements](#switch-statements) - [Loops](#loops) - [The Loop Variable](#the-loop-variable) - [Comments](#comments) - - [PHP](#php) + - [Including Subviews](#including-subviews) - [The `@once` Directive](#the-once-directive) -- [Including Subviews](#including-subviews) - - [Rendering Views For Collections](#rendering-views-for-collections) -- [Forms](#forms) - - [CSRF Field](#csrf-field) - - [Method Field](#method-field) - - [Validation Errors](#validation-errors) + - [Raw PHP](#raw-php) - [Components](#components) - [Rendering Components](#rendering-components) - [Passing Data To Components](#passing-data-to-components) @@ -29,8 +21,14 @@ - [Inline Component Views](#inline-component-views) - [Anonymous Components](#anonymous-components) - [Dynamic Components](#dynamic-components) - - [Components As Layouts](#components-as-layouts) - [Manually Registering Components](#manually-registering-components) +- [Building Layouts](#building-layouts) + - [Layouts Using Components](#layouts-using-components) + - [Layouts Using Template Inheritance](#layouts-using-template-inheritance) +- [Forms](#forms) + - [CSRF Field](#csrf-field) + - [Method Field](#method-field) + - [Validation Errors](#validation-errors) - [Stacks](#stacks) - [Service Injection](#service-injection) - [Extending Blade](#extending-blade) @@ -41,72 +39,14 @@ Blade is the simple, yet powerful templating engine that is included with Laravel. Unlike some PHP templating engines, Blade does not restrict you from using plain PHP code in your templates. In fact, all Blade templates are compiled into plain PHP code and cached until they are modified, meaning Blade adds essentially zero overhead to your application. Blade template files use the `.blade.php` file extension and are typically stored in the `resources/views` directory. -Blade views may be returned from routes or controller using the global `view` helper: +Blade views may be returned from routes or controller using the global `view` helper. Of course, as mentioned in the documentation on [views](/docs/{{version}}/views), data may be passed to the view using the `view` helper's second argument: Route::get('/', function () { - return view('greeting'); + return view('greeting', ['name' => 'Finn']); }); > {tip} Before digging deeper into Blade, make sure to read the Laravel [view documentation](/docs/{{version}}/views). - -## Template Inheritance - - -### Defining A Layout - -Two of the primary benefits of using Blade are _template inheritance_ and _sections_. To get started, let's take a look at a simple example. First, we will examine a page layout. Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Blade view: - - - - - - App Name - @yield('title') - - - @section('sidebar') - This is the master sidebar. - @show - -
- @yield('content') -
- - - -As you can see, this file contains typical HTML mark-up. However, take note of the `@section` and `@yield` directives. The `@section` directive, as the name implies, defines a section of content, while the `@yield` directive is used to display the contents of a given section. - -Now that we have defined a layout for our application, let's define a child page that inherits the layout. - - -### Extending A Layout - -When defining a child view, use the `@extends` Blade directive to specify which layout the child view should "inherit". Views which extend a Blade layout may inject content into the layout's sections using `@section` directives. Remember, as seen in the example above, the contents of these sections will be displayed in the layout using `@yield`: - - - - @extends('layouts.app') - - @section('title', 'Page Title') - - @section('sidebar') - @@parent - -

This is appended to the master sidebar.

- @endsection - - @section('content') -

This is my body content.

- @endsection - -In this example, the `sidebar` section is utilizing the `@@parent` directive to append (rather than overwriting) content to the layout's sidebar. The `@@parent` directive will be replaced by the content of the layout when the view is rendered. - -> {tip} Contrary to the previous example, this `sidebar` section ends with `@endsection` instead of `@show`. The `@endsection` directive will only define a section while `@show` will define and **immediately yield** the section. - -The `@yield` directive also accepts a default value as its second parameter. This value will be rendered if the section being yielded is undefined: - - @yield('content', 'Default content') - ## Displaying Data @@ -209,8 +149,8 @@ If you are displaying JavaScript variables in a large portion of your template,
@endverbatim - -## Control Structures + +## Blade Directives 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. @@ -422,30 +362,8 @@ 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 - $counter = 1; - @endphp - - -### The `@once` Directive - -The `@once` directive allows you to define a portion of the template that will only be evaluated once per rendering cycle. This may be useful for pushing a given piece of JavaScript into the page's header using [stacks](#stacks). For example, if you are rendering a given [component](#components) within a loop, you may wish to only push the JavaScript to the header the first time the component is rendered: - - @once - @push('scripts') - - @endpush - @endonce - -## Including Subviews +### Including Subviews > {tip} While you're free to use the `@include` directive, Blade [components](#components) provide similar functionality and offer several benefits over the `@include` directive such as data and attribute binding. @@ -480,7 +398,7 @@ To include the first view that exists from a given array of views, you may use t > {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. -### Rendering Views For Collections +#### Rendering Views For Collections You may combine loops and includes into one line with Blade's `@each` directive: @@ -494,6 +412,99 @@ You may also pass a fourth argument to the `@each` directive. This argument dete > {note} Views rendered via `@each` do not inherit the variables from the parent view. If the child view requires these variables, you should use the `@foreach` and `@include` directives instead. + +### The `@once` Directive + +The `@once` directive allows you to define a portion of the template that will only be evaluated once per rendering cycle. This may be useful for pushing a given piece of JavaScript into the page's header using [stacks](#stacks). For example, if you are rendering a given [component](#components) within a loop, you may wish to only push the JavaScript to the header the first time the component is rendered: + + @once + @push('scripts') + + @endpush + @endonce + + +## Building Layouts + + +### Layouts Using Components + +We previously discussed [template inheritance](#template-inheritance) in this documentation. However, if you prefer, you may use components to achieve the same goals as template inheritance. For example, imagine a `layout` component that looks like the following: + + + +

Application

+
+ {{ $slot }} + + + +Once the `layout` component has been defined, we may create a Blade template that utilizes the `layout` component to achieve the same goals as template inheritance: + + + Page content... + + + +### Layouts Using Template Inheritance + + +#### Defining A Layout + +Two of the primary benefits of using Blade are _template inheritance_ and _sections_. To get started, let's take a look at a simple example. First, we will examine a page layout. Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Blade view: + + + + + + App Name - @yield('title') + + + @section('sidebar') + This is the master sidebar. + @show + +
+ @yield('content') +
+ + + +As you can see, this file contains typical HTML mark-up. However, take note of the `@section` and `@yield` directives. The `@section` directive, as the name implies, defines a section of content, while the `@yield` directive is used to display the contents of a given section. + +Now that we have defined a layout for our application, let's define a child page that inherits the layout. + + +#### Extending A Layout + +When defining a child view, use the `@extends` Blade directive to specify which layout the child view should "inherit". Views which extend a Blade layout may inject content into the layout's sections using `@section` directives. Remember, as seen in the example above, the contents of these sections will be displayed in the layout using `@yield`: + + + + @extends('layouts.app') + + @section('title', 'Page Title') + + @section('sidebar') + @@parent + +

This is appended to the master sidebar.

+ @endsection + + @section('content') +

This is my body content.

+ @endsection + +In this example, the `sidebar` section is utilizing the `@@parent` directive to append (rather than overwriting) content to the layout's sidebar. The `@@parent` directive will be replaced by the content of the layout when the view is rendered. + +> {tip} Contrary to the previous example, this `sidebar` section ends with `@endsection` instead of `@show`. The `@endsection` directive will only define a section while `@show` will define and **immediately yield** the section. + +The `@yield` directive also accepts a default value as its second parameter. This value will be rendered if the section being yielded is undefined: + + @yield('content', 'Default content') + ## Forms @@ -546,6 +557,15 @@ You may pass [the name of a specific error bag](/docs/{{version}}/validation#nam
{{ $message }}
@enderror + +### Raw 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 + $counter = 1; + @endphp + ## Components @@ -909,25 +929,6 @@ Sometimes you may need to render a component but not know which component should - -### Components As Layouts - -We previously discussed [template inheritance](#template-inheritance) in this documentation. However, if you prefer, you may use components to achieve the same goals as template inheritance. For example, imagine a `layout` component that looks like the following: - - - -

Application

-
- {{ $slot }} - - - -Once the `layout` component has been defined, we may create a Blade template that utilizes the `layout` component to achieve the same goals as template inheritance: - - - Page content... - - ### Manually Registering Components From a0f1ab5043d566b00be3a23811f5d9e7c926340a Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 14 Nov 2020 14:45:52 -0600 Subject: [PATCH 074/274] work on layout docs --- blade.md | 59 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/blade.md b/blade.md index 7c7e74c8ae..a035049fe7 100644 --- a/blade.md +++ b/blade.md @@ -39,7 +39,7 @@ Blade is the simple, yet powerful templating engine that is included with Laravel. Unlike some PHP templating engines, Blade does not restrict you from using plain PHP code in your templates. In fact, all Blade templates are compiled into plain PHP code and cached until they are modified, meaning Blade adds essentially zero overhead to your application. Blade template files use the `.blade.php` file extension and are typically stored in the `resources/views` directory. -Blade views may be returned from routes or controller using the global `view` helper. Of course, as mentioned in the documentation on [views](/docs/{{version}}/views), data may be passed to the view using the `view` helper's second argument: +Blade views may be returned from routes or controller using the global `view` helper. Of course, as mentioned in the documentation on [views](/docs/{{version}}/views), data may be passed to the Blade view using the `view` helper's second argument: Route::get('/', function () { return view('greeting', ['name' => 'Finn']); @@ -431,31 +431,72 @@ The `@once` directive allows you to define a portion of the template that will o ### Layouts Using Components -We previously discussed [template inheritance](#template-inheritance) in this documentation. However, if you prefer, you may use components to achieve the same goals as template inheritance. For example, imagine a `layout` component that looks like the following: +Most web applications maintain the same general layout across various pages. It would be incredibly cumbersome and hard to maintain our application if we had to repeat the entire layout HTML in every view we create. Thankfully, it's convenient to define this layout as a single [Blade component](#components) and then use it throughout our application. + + +#### Defining The Layout Component + +For example, imagine we are building a "todo" list application. We might define a `layout` component that looks like the following: + + + + {{ $title ?? 'Todo Manager' }} + </head> <body> - <h1>Application</h1> - <hr> + <h1>Todos</h1> + <hr/> {{ $slot }} </body> </html> -Once the `layout` component has been defined, we may create a Blade template that utilizes the `layout` component to achieve the same goals as template inheritance: +<a name="applying-the-layout-component"></a> +#### Applying The Layout Component + +Once the `layout` component has been defined, we may create a Blade view that utilizes the component. In this example, we will define a simple view that displays our task list: + + <!-- resources/views/tasks.blade.php --> + + <x-layout> + @foreach ($tasks as $task) + {{ $task }} + @endforeach + </x-layout> + +Remember, content that is injected into a component will be supplied to the default `$slot` variable within our `layout` component. As you may have noticed, our `layout` also respects a `$title` slot if one is provided; otherwise, a default title is shown. We may inject a custom title from our task list view using the standard slot syntax discussed in the [component documentation](#components): + + <!-- resources/views/tasks.blade.php --> <x-layout> - Page content... + <x-slot name="title"> + Custom Title + </x-slot> + + @foreach ($tasks as $task) + {{ $task }} + @endforeach </x-layout> +Now that we have defined our layout and task list views, we just need to return the `task` view from a route: + + use App\Models\Task; + + Route::get('/tasks', function () { + return view('tasks', ['tasks' => Task::all()]); + }); + <a name="layouts-using-template-inheritance"></a> ### Layouts Using Template Inheritance <a name="defining-a-layout"></a> #### Defining A Layout -Two of the primary benefits of using Blade are _template inheritance_ and _sections_. To get started, let's take a look at a simple example. First, we will examine a page layout. Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Blade view: +Layouts may also be created via "template inheritance". This was the primary way of building applications prior to the introduction of [components](#components). + +To get started, let's take a look at a simple example. First, we will examine a page layout. Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Blade view: - <!-- Stored in resources/views/layouts/app.blade.php --> + <!-- resources/views/layouts/app.blade.php --> <html> <head> @@ -481,7 +522,7 @@ Now that we have defined a layout for our application, let's define a child page When defining a child view, use the `@extends` Blade directive to specify which layout the child view should "inherit". Views which extend a Blade layout may inject content into the layout's sections using `@section` directives. Remember, as seen in the example above, the contents of these sections will be displayed in the layout using `@yield`: - <!-- Stored in resources/views/child.blade.php --> + <!-- resources/views/child.blade.php --> @extends('layouts.app') From 615acdc74a261c04db67d44de2a1dc1d477c7ecb Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 11:48:15 -0600 Subject: [PATCH 075/274] fortify note --- sanctum.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sanctum.md b/sanctum.md index cdc0a68b26..f28220f21a 100644 --- a/sanctum.md +++ b/sanctum.md @@ -238,7 +238,7 @@ During this request Laravel will set an `XSRF-TOKEN` cookie containing the curre <a name="logging-in"></a> #### Logging In -Once CSRF protection has been initialized, you should make a `POST` request to the your Laravel application's `/login` route. This `/login` route may be implemented manually or using one of Laravel's [application starter kits](/docs/{{version}}/starter-kits). +Once CSRF protection has been initialized, you should make a `POST` request to the your Laravel application's `/login` route. This `/login` route may be [implemented manually](/docs/{{version}}/authentication#authenticating-users) or using a package like [Laravel Fortify](https://github.com/laravel/fortify). If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client. In addition, since your application already made a request to the `/sanctum/csrf-cookie` route, subsequent requests should automatically receive CSRF protection as long as your JavaScript HTTP client sends the value of `XSRF-TOKEN` cookie in the `X-XSRF-TOKEN` header. From b5582181c435438d009a682a4a3f815d8df0584b Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 13:15:13 -0600 Subject: [PATCH 076/274] wip --- urls.md | 52 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/urls.md b/urls.md index cd9c922630..e3ef9fab3d 100644 --- a/urls.md +++ b/urls.md @@ -2,7 +2,7 @@ - [Introduction](#introduction) - [The Basics](#the-basics) - - [Generating Basic URLs](#generating-basic-urls) + - [Generating URLs](#generating-urls) - [Accessing The Current URL](#accessing-the-current-url) - [URLs For Named Routes](#urls-for-named-routes) - [Signed URLs](#signed-urls) @@ -12,15 +12,15 @@ <a name="introduction"></a> ## Introduction -Laravel provides several helpers to assist you in generating URLs for your application. These are mainly helpful when building links in your templates and API responses, or when generating redirect responses to another part of your application. +Laravel provides several helpers to assist you in generating URLs for your application. These helpers are primarily helpful when building links in your templates and API responses, or when generating redirect responses to another part of your application. <a name="the-basics"></a> ## The Basics -<a name="generating-basic-urls"></a> -### Generating Basic URLs +<a name="generating-urls"></a> +### Generating URLs -The `url` helper may be used to generate arbitrary URLs for your application. The generated URL will automatically use the scheme (HTTP or HTTPS) and host from the current request: +The `url` helper may be used to generate arbitrary URLs for your application. The generated URL will automatically use the scheme (HTTP or HTTPS) and host from the current request being handled by the application: $post = App\Models\Post::find(1); @@ -51,7 +51,7 @@ Each of these methods may also be accessed via the `URL` [facade](/docs/{{versio <a name="urls-for-named-routes"></a> ## URLs For Named Routes -The `route` helper may be used to generate URLs to named routes. Named routes allow you to generate URLs without being coupled to the actual URL defined on the route. Therefore, if the route's URL changes, no changes need to be made to your `route` function calls. For example, imagine your application contains a route defined like the following: +The `route` helper may be used to generate URLs to [named routes](/docs/{{version}}/routing#named-routes). Named routes allow you to generate URLs without being coupled to the actual URL defined on the route. Therefore, if the route's URL changes, no changes need to be made to your calls to the `route` function. For example, imagine your application contains a route defined like the following: Route::get('/post/{post}', function () { // @@ -63,17 +63,7 @@ To generate a URL to this route, you may use the `route` helper like so: // http://example.com/post/1 -Any additional array parameters that do not correspond to the route's definition parameters will be added to the URL's query string: - - echo route('post.show', ['post' => 1, 'search' => 'rocket']); - - // http://example.com/post/1?search=rocket - -You will often be generating URLs using the primary key of [Eloquent models](/docs/{{version}}/eloquent). For this reason, you may pass Eloquent models as parameter values. The `route` helper will automatically extract the model's primary key: - - echo route('post.show', ['post' => $post]); - -The `route` helper may also be used to generate URLs for routes with multiple parameters: +Of course, the `route` helper may also be used to generate URLs for routes with multiple parameters: Route::get('/post/{post}/comment/{comment}', function () { // @@ -83,6 +73,19 @@ The `route` helper may also be used to generate URLs for routes with multiple pa // http://example.com/post/1/comment/3 +Any additional array elements that do not correspond to the route's definition parameters will be added to the URL's query string: + + echo route('post.show', ['post' => 1, 'search' => 'rocket']); + + // http://example.com/post/1?search=rocket + +<a name="eloquent-models"></a> +#### Eloquent Models + +You will often be generating URLs using the primary key of [Eloquent models](/docs/{{version}}/eloquent). For this reason, you may pass Eloquent models as parameter values. The `route` helper will automatically extract the model's primary key: + + echo route('post.show', ['post' => $post]); + <a name="signed-urls"></a> ### Signed URLs @@ -94,7 +97,7 @@ For example, you might use signed URLs to implement a public "unsubscribe" link return URL::signedRoute('unsubscribe', ['user' => 1]); -If you would like to generate a temporary signed route URL that expires, you may use the `temporarySignedRoute` method: +If you would like to generate a temporary signed route URL that expires after a specified amount of time, you may use the `temporarySignedRoute` method. When Laravel validates a temporary signed route URL, it will ensure that the expiration timestamp that is encoded into the signed URL has not elapsed: use Illuminate\Support\Facades\URL; @@ -117,7 +120,7 @@ To verify that an incoming request has a valid signature, you should call the `h // ... })->name('unsubscribe'); -Alternatively, you may assign the `Illuminate\Routing\Middleware\ValidateSignature` middleware to the route. If it is not already present, you should assign this middleware a key in your HTTP kernel's `routeMiddleware` array: +Alternatively, you may assign the `Illuminate\Routing\Middleware\ValidateSignature` [middleware](/docs/{{version}}/middleware) to the route. If it is not already present, you should assign this middleware a key in your HTTP kernel's `routeMiddleware` array: /** * The application's route middleware. @@ -130,7 +133,7 @@ Alternatively, you may assign the `Illuminate\Routing\Middleware\ValidateSignatu 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, ]; -Once you have registered the middleware in your kernel, you may attach it to a route. If the incoming request does not have a valid signature, the middleware will automatically return a `403` error response: +Once you have registered the middleware in your kernel, you may attach it to a route. If the incoming request does not have a valid signature, the middleware will automatically return a `403` HTTP response: Route::post('/unsubscribe/{user}', function (Request $request) { // ... @@ -145,7 +148,7 @@ The `action` function generates a URL for the given controller action: $url = action([HomeController::class, 'index']); -If the controller method accepts route parameters, you may pass them as the second argument to the function: +If the controller method accepts route parameters, you may pass an associative array of route parameters as the second argument to the function: $url = action([UserController::class, 'profile'], ['id' => 1]); @@ -169,6 +172,13 @@ It is cumbersome to always pass the `locale` every time you call the `route` hel class SetDefaultLocaleForUrls { + /** + * Handle the incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure $next + * @return \Illuminate\Http\Response + */ public function handle($request, Closure $next) { URL::defaults(['locale' => $request->user()->locale]); From 94750177cc2b67607e1d04527cd0f52da950280d Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 13:30:43 -0600 Subject: [PATCH 077/274] wip --- session.md | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/session.md b/session.md index 0990d199f3..fd835422d9 100644 --- a/session.md +++ b/session.md @@ -3,7 +3,7 @@ - [Introduction](#introduction) - [Configuration](#configuration) - [Driver Prerequisites](#driver-prerequisites) -- [Using The Session](#using-the-session) +- [Interacting With The Session](#interacting-with-the-session) - [Retrieving Data](#retrieving-data) - [Storing Data](#storing-data) - [Flash Data](#flash-data) @@ -17,12 +17,14 @@ <a name="introduction"></a> ## 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](https://memcached.org), [Redis](https://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. That user information is typically placed in a persistent store / backend that can be accessed from subsequent 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](https://redis.io), and databases is included. <a name="configuration"></a> ### Configuration -The session configuration file is stored at `config/session.php`. Be sure to review the options available to you in this file. By default, Laravel is configured to use the `file` session driver, which will work well for many applications. +Your application's session configuration file is stored at `config/session.php`. Be sure to review the options available to you in this file. By default, Laravel is configured to use the `file` session driver, which will work well for many applications. If your application will be load balanced across multiple web servers, you should choose a centralized store that all servers can access, such as Redis or a database. The session `driver` configuration option defines where session data will be stored for each request. Laravel ships with several great drivers out of the box: @@ -34,7 +36,7 @@ The session `driver` configuration option defines where session data will be sto - `array` - sessions are stored in a PHP array and will not be persisted. </div> -> {tip} The array driver is used during [testing](/docs/{{version}}/testing) and prevents the data stored in the session from being persisted. +> {tip} The array driver is primarily used during [testing](/docs/{{version}}/testing) and prevents the data stored in the session from being persisted. <a name="driver-prerequisites"></a> ### Driver Prerequisites @@ -42,7 +44,7 @@ The session `driver` configuration option defines where session data will be sto <a name="database"></a> #### Database -When using the `database` session driver, you will need to create a table to contain the session items. Below is an example `Schema` declaration for the table: +When using the `database` session driver, you will need to create a table to contain the session records. An example `Schema` declaration for the table may be found below: Schema::create('sessions', function ($table) { $table->string('id')->unique(); @@ -53,7 +55,7 @@ When using the `database` session driver, you will need to create a table to con $table->integer('last_activity'); }); -You may use the `session:table` Artisan command to generate this migration: +You may use the `session:table` Artisan command to generate this migration. To learn more about database migrations, you may consult the complete [migration documentation](/docs/{{version}}/migrations): php artisan session:table @@ -62,17 +64,17 @@ You may use the `session:table` Artisan command to generate this migration: <a name="redis"></a> #### Redis -Before using Redis sessions with Laravel, you will need to either install the PhpRedis PHP extension via PECL or install the `predis/predis` package (~1.0) via Composer. For more information on configuring Redis, consult its [Laravel documentation page](/docs/{{version}}/redis#configuration). +Before using Redis sessions with Laravel, you will need to either install the PhpRedis PHP extension via PECL or install the `predis/predis` package (~1.0) via Composer. For more information on configuring Redis, consult Laravel's [Redis documentation](/docs/{{version}}/redis#configuration). > {tip} In the `session` configuration file, the `connection` option may be used to specify which Redis connection is used by the session. -<a name="using-the-session"></a> -## Using The Session +<a name="interacting-with-the-session"></a> +## Interacting With The Session <a name="retrieving-data"></a> ### Retrieving Data -There are two primary ways of working with session data in Laravel: the global `session` helper and via a `Request` instance. First, let's look at accessing the session via a `Request` instance, which can be type-hinted on a controller method. Remember, controller method dependencies are automatically injected via the Laravel [service container](/docs/{{version}}/container): +There are two primary ways of working with session data in Laravel: the global `session` helper and via a `Request` instance. First, let's look at accessing the session via a `Request` instance, which can be type-hinted on a route closure or controller method. Remember, controller method dependencies are automatically injected via the Laravel [service container](/docs/{{version}}/container): <?php @@ -111,7 +113,7 @@ When you retrieve an item from the session, you may also pass a default value as You may also use the global `session` PHP function to retrieve and store data in the session. When the `session` helper is called with a single, string argument, it will return the value of that session key. When the helper is called with an array of key / value pairs, those values will be stored in the session: - Route::get('home', function () { + Route::get('/home', function () { // Retrieve a piece of data from the session... $value = session('key'); @@ -140,7 +142,7 @@ To determine if an item is present in the session, you may use the `has` method. // } -To determine if an item is present in the session, even if its value is `null`, you may use the `exists` method. The `exists` method returns `true` if the item is present: +To determine if an item is present in the session, even if its value is `null`, you may use the `exists` method: if ($request->session()->exists('users')) { // @@ -149,12 +151,12 @@ To determine if an item is present in the session, even if its value is `null`, <a name="storing-data"></a> ### Storing Data -To store data in the session, you will typically use the `put` method or the `session` helper: +To store data in the session, you will typically use the request instance's `put` method or the `session` helper: // Via a request instance... $request->session()->put('key', 'value'); - // Via the global helper... + // Via the global "session" helper... session(['key' => 'value']); <a name="pushing-to-array-session-values"></a> @@ -174,11 +176,11 @@ The `pull` method will retrieve and delete an item from the session in a single <a name="flash-data"></a> ### Flash Data -Sometimes you may wish to store items in the session only for the next request. You may do so using the `flash` method. Data stored in the session using this method will be available immediately and during the subsequent HTTP request. After the subsequent HTTP request, the flashed data will be deleted. Flash data is primarily useful for short-lived status messages: +Sometimes you may wish to store items in the session for the next request. You may do so using the `flash` method. Data stored in the session using this method will be available immediately and during the subsequent HTTP request. After the subsequent HTTP request, the flashed data will be deleted. Flash data is primarily useful for short-lived status messages: $request->session()->flash('status', 'Task was successful!'); -If you need to keep your flash data around for several requests, you may use the `reflash` method, which will keep all of the flash data for an additional request. If you only need to keep specific flash data, you may use the `keep` method: +If you need to persist your flash data for several requests, you may use the `reflash` method, which will keep all of the flash data for an additional request. If you only need to keep specific flash data, you may use the `keep` method: $request->session()->reflash(); @@ -190,10 +192,10 @@ If you need to keep your flash data around for several requests, you may use the The `forget` method will remove a piece of data from the session. If you would like to remove all data from the session, you may use the `flush` method: // Forget a single key... - $request->session()->forget('key'); + $request->session()->forget('name'); // Forget multiple keys... - $request->session()->forget(['key1', 'key2']); + $request->session()->forget(['name', 'status']); $request->session()->flush(); @@ -202,7 +204,7 @@ The `forget` method will remove a piece of data from the session. If you would l Regenerating the session ID is often done in order to prevent malicious users from exploiting a [session fixation](https://owasp.org/www-community/attacks/Session_fixation) attack on your application. -Laravel automatically regenerates the session ID during authentication if you are using one of the Laravel [application starter kits](/docs/{{version}}/starter-kits); however, if you need to manually regenerate the session ID, you may use the `regenerate` method. +Laravel automatically regenerates the session ID during authentication if you are using one of the Laravel [application starter kits](/docs/{{version}}/starter-kits) or [Laravel Fortify](https://github.com/laravel/fortify); however, if you need to manually regenerate the session ID, you may use the `regenerate` method. $request->session()->regenerate(); @@ -239,7 +241,7 @@ If neither of these arguments are passed, the lock will be obtained for a maximu <a name="implementing-the-driver"></a> #### Implementing The Driver -Your custom session driver should implement the `SessionHandlerInterface`. This interface contains just a few simple methods we need to implement. A stubbed MongoDB implementation looks something like this: +If none of the existing session drivers fit your application's needs, Laravel makes it possible to write your own session handler. Your custom session driver should implement PHP's built-in `SessionHandlerInterface`. This interface contains just a few simple methods. A stubbed MongoDB implementation looks like the following: <?php @@ -260,10 +262,10 @@ Your custom session driver should implement the `SessionHandlerInterface`. This Since the purpose of these methods is not readily understandable, let's quickly cover what each of the methods do: <div class="content-list" markdown="1"> -- The `open` method would typically be used in file based session store systems. Since Laravel ships with a `file` session driver, you will almost never need to put anything in this method. You can leave it as an empty stub. It is a fact of poor interface design (which we'll discuss later) that PHP requires us to implement this method. +- The `open` method would typically be used in file based session store systems. Since Laravel ships with a `file` session driver, you will almost never need to put anything in this method. You can simply leave this method empty. - The `close` method, like the `open` method, can also usually be disregarded. For most drivers, it is not needed. - The `read` method should return the string version of the session data associated with the given `$sessionId`. There is no need to do any serialization or other encoding when retrieving or storing session data in your driver, as Laravel will perform the serialization for you. -- The `write` method should write the given `$data` string associated with the `$sessionId` to some persistent storage system, such as MongoDB, Dynamo, etc. Again, you should not perform any serialization - Laravel will have already handled that for you. +- The `write` method should write the given `$data` string associated with the `$sessionId` to some persistent storage system, such as MongoDB or another storage system of your choice. Again, you should not perform any serialization - Laravel will have already handled that for you. - The `destroy` method should remove the data associated with the `$sessionId` from persistent storage. - The `gc` method should destroy all session data that is older than the given `$lifetime`, which is a UNIX timestamp. For self-expiring systems like Memcached and Redis, this method may be left empty. </div> @@ -271,7 +273,7 @@ Since the purpose of these methods is not readily understandable, let's quickly <a name="registering-the-driver"></a> #### Registering The Driver -Once your driver has been implemented, you are ready to register it with the framework. To add additional drivers to Laravel's session backend, you may use the `extend` method on the `Session` [facade](/docs/{{version}}/facades). You should call the `extend` method from the `boot` method of a [service provider](/docs/{{version}}/providers). You may do this from the existing `AppServiceProvider` or create an entirely new provider: +Once your driver has been implemented, you are ready to register it with Laravel. To add additional drivers to Laravel's session backend, you may use the `extend` method provided by the `Session` [facade](/docs/{{version}}/facades). You should call the `extend` method from the `boot` method of a [service provider](/docs/{{version}}/providers). You may do this from the existing `App\Providers\AppServiceProvider` or create an entirely new provider: <?php @@ -301,7 +303,7 @@ Once your driver has been implemented, you are ready to register it with the fra public function boot() { Session::extend('mongo', function ($app) { - // Return implementation of SessionHandlerInterface... + // Return an implementation of SessionHandlerInterface... return new MongoSessionHandler; }); } From 0c4c52493309da330b92df4ad92a9df56a0ddb93 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 13:44:54 -0600 Subject: [PATCH 078/274] wip --- validation.md | 89 +++++++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/validation.md b/validation.md index f0f7b39625..c39c3ee917 100644 --- a/validation.md +++ b/validation.md @@ -31,12 +31,14 @@ <a name="introduction"></a> ## Introduction -Laravel provides several different approaches to validate your application's incoming data. By default, Laravel's base controller class uses a `ValidatesRequests` trait which provides a convenient method to validate incoming HTTP requests with a variety of powerful validation rules. +Laravel provides several different approaches to validate your application's incoming data. It is most common to use the `validate` method available on all incoming HTTP requests. However, we will discuss other approaches to validation as well. + +Laravel includes a wide variety of convenient validation rules that you may apply to data, even providing the ability to validate if values are unique in a given database table. We'll cover each of these validation rules in detail so that you are familiar with all of Laravel's validation features. <a name="validation-quickstart"></a> ## Validation Quickstart -To learn about Laravel's powerful validation features, let's look at a complete example of validating a form and displaying the error messages back to the user. +To learn about Laravel's powerful validation features, let's look at a complete example of validating a form and displaying the error messages back to the user. By reading this high-level overview, you'll be able to gain a good general understanding of how to validate incoming request data using Laravel: <a name="quick-defining-the-routes"></a> ### Defining The Routes @@ -45,16 +47,15 @@ First, let's assume we have the following routes defined in our `routes/web.php` use App\Http\Controllers\PostController; - Route::get('post/create', [PostController::class, 'create']); - - Route::post('post', [PostController::class, 'store']); + Route::get('/post/create', [PostController::class, 'create']); + Route::post('/post', [PostController::class, 'store']); The `GET` route will display a form for the user to create a new blog post, while the `POST` route will store the new blog post in the database. <a name="quick-creating-the-controller"></a> ### Creating The Controller -Next, let's take a look at a simple controller that handles these routes. We'll leave the `store` method empty for now: +Next, let's take a look at a simple controller that handles incoming requests to these routes. We'll leave the `store` method empty for now: <?php @@ -68,7 +69,7 @@ Next, let's take a look at a simple controller that handles these routes. We'll /** * Show the form to create a new blog post. * - * @return Response + * @return \Illuminate\View\View */ public function create() { @@ -78,8 +79,8 @@ Next, let's take a look at a simple controller that handles these routes. We'll /** * Store a new blog post. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function store(Request $request) { @@ -90,19 +91,19 @@ Next, let's take a look at a simple controller that handles these routes. We'll <a name="quick-writing-the-validation-logic"></a> ### Writing The Validation Logic -Now we are ready to fill in our `store` method with the logic to validate the new blog post. To do this, we will use the `validate` method provided by the `Illuminate\Http\Request` object. If the validation rules pass, your code will keep executing normally; however, if validation fails, an exception will be thrown and the proper error response will automatically be sent back to the user. In the case of a traditional HTTP request, a redirect response will be generated, while a JSON response will be sent for AJAX requests. +Now we are ready to fill in our `store` method with the logic to validate the new blog post. To do this, we will use the `validate` method provided by the `Illuminate\Http\Request` object. If the validation rules pass, your code will keep executing normally; however, if validation fails, an exception will be thrown and the proper error response will automatically be sent back to the user. A redirect response to the previous URL will be generated in the case of a traditional HTTP request. If the incoming request is an XHR request, a JSON response containing the validation error messages will be returned. To get a better understanding of the `validate` method, let's jump back into the `store` method: /** * Store a new blog post. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function store(Request $request) { - $validatedData = $request->validate([ + $validated = $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]); @@ -110,7 +111,7 @@ To get a better understanding of the `validate` method, let's jump back into the // The blog post is valid... } -As you can see, we pass the desired validation rules into the `validate` method. Again, if the validation fails, the proper response will automatically be generated. If the validation passes, our controller will continue executing normally. +As you can see, the validation rules are passed into the `validate` method. Don't worry - all available validation rules are [documented](#available-validation-rules). Again, if the validation fails, the proper response will automatically be generated. If the validation passes, our controller will continue executing normally. Alternatively, validation rules may be specified as arrays of rules instead of a single `|` delimited string: @@ -119,7 +120,7 @@ Alternatively, validation rules may be specified as arrays of rules instead of a 'body' => ['required'], ]); -You may use the `validateWithBag` method to validate a request and store any error messages within a [named error bag](#named-error-bags): +In addition, you may use the `validateWithBag` method to validate a request and store any error messages within a [named error bag](#named-error-bags): $validatedData = $request->validateWithBag('post', [ 'title' => ['required', 'unique:posts', 'max:255'], @@ -141,7 +142,7 @@ In this example, if the `unique` rule on the `title` attribute fails, the `max` <a name="a-note-on-nested-attributes"></a> #### A Note On Nested Attributes -If your HTTP request contains "nested" parameters, you may specify them in your validation rules using "dot" syntax: +If the incoming HTTP request contains "nested" field data, you may specify these fields in your validation rules using "dot" syntax: $request->validate([ 'title' => 'required|unique:posts|max:255', @@ -159,44 +160,53 @@ On the other hand, if your field name contains a literal period, you can explici <a name="quick-displaying-the-validation-errors"></a> ### Displaying The Validation Errors -So, what if the incoming request parameters do not pass the given validation rules? As mentioned previously, Laravel will automatically redirect the user back to their previous location. In addition, all of the validation errors will automatically be [flashed to the session](/docs/{{version}}/session#flash-data). +So, what if the incoming request fields do not pass the given validation rules? As mentioned previously, Laravel will automatically redirect the user back to their previous location. In addition, all of the validation errors will automatically be [flashed to the session](/docs/{{version}}/session#flash-data). -Again, notice that we did not have to explicitly bind the error messages to the view in our `GET` route. This is because Laravel will check for errors in the session data, and automatically bind them to the view if they are available. The `$errors` variable will be an instance of `Illuminate\Support\MessageBag`. For more information on working with this object, [check out its documentation](#working-with-error-messages). +Note that we do not have to explicitly bind the error messages to the view in our `GET` route. This is because Laravel will check for errors in the session data, and automatically bind them to the view if they are available. The `$errors` variable will be an instance of `Illuminate\Support\MessageBag`. For more information on working with this object, [check out its documentation](#working-with-error-messages). > {tip} The `$errors` variable is bound to the view by the `Illuminate\View\Middleware\ShareErrorsFromSession` middleware, which is provided by the `web` middleware group. **When this middleware is applied an `$errors` variable will always be available in your views**, allowing you to conveniently assume the `$errors` variable is always defined and can be safely used. So, in our example, the user will be redirected to our controller's `create` method when validation fails, allowing us to display the error messages in the view: - <!-- /resources/views/post/create.blade.php --> +```html +<!-- /resources/views/post/create.blade.php --> + +<h1>Create Post</h1> + +@if ($errors->any()) + <div class="alert alert-danger"> + <ul> + @foreach ($errors->all() as $error) + <li>{{ $error }}</li> + @endforeach + </ul> + </div> +@endif - <h1>Create Post</h1> +<!-- Create Post Form --> +``` - @if ($errors->any()) - <div class="alert alert-danger"> - <ul> - @foreach ($errors->all() as $error) - <li>{{ $error }}</li> - @endforeach - </ul> - </div> - @endif +<a name="quick-xhr-requests-and-validation"></a> +#### XHR Requests & Validation - <!-- Create Post Form --> +In this example, we used a traditional form to send data to the application. However, many applications receive XHR requests from a JavaScript powered frontend. When using the `validate` method during an XHR request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a `422` HTTP status code. <a name="the-at-error-directive"></a> #### The `@error` Directive -You may also use the `@error` [Blade](/docs/{{version}}/blade) directive to quickly check if validation error messages exist for a given attribute. Within an `@error` directive, you may echo the `$message` variable to display the error message: +You may use the `@error` [Blade](/docs/{{version}}/blade) directive to quickly determine if validation error messages exist for a given attribute. Within an `@error` directive, you may echo the `$message` variable to display the error message: - <!-- /resources/views/post/create.blade.php --> +```html +<!-- /resources/views/post/create.blade.php --> - <label for="title">Post Title</label> +<label for="title">Post Title</label> - <input id="title" type="text" class="@error('title') is-invalid @enderror"> +<input id="title" type="text" class="@error('title') is-invalid @enderror"> - @error('title') - <div class="alert alert-danger">{{ $message }}</div> - @enderror +@error('title') + <div class="alert alert-danger">{{ $message }}</div> +@enderror +``` <a name="a-note-on-optional-fields"></a> ### A Note On Optional Fields @@ -211,11 +221,6 @@ By default, Laravel includes the `TrimStrings` and `ConvertEmptyStringsToNull` m In this example, we are specifying that the `publish_at` field may be either `null` or a valid date representation. If the `nullable` modifier is not added to the rule definition, the validator would consider `null` an invalid date. -<a name="quick-ajax-requests-and-validation"></a> -#### AJAX Requests & Validation - -In this example, we used a traditional form to send data to the application. However, many applications use AJAX requests. When using the `validate` method during an AJAX request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a 422 HTTP status code. - <a name="form-request-validation"></a> ## Form Request Validation From 1f93c8e532445c4aeb59ffd5fbbee415094fbb68 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 14:23:08 -0600 Subject: [PATCH 079/274] wip --- validation.md | 189 ++++++++++++++++++++++++++------------------------ 1 file changed, 99 insertions(+), 90 deletions(-) diff --git a/validation.md b/validation.md index c39c3ee917..f1ca184fc6 100644 --- a/validation.md +++ b/validation.md @@ -11,14 +11,14 @@ - [Creating Form Requests](#creating-form-requests) - [Authorizing Form Requests](#authorizing-form-requests) - [Customizing The Error Messages](#customizing-the-error-messages) - - [Customizing The Validation Attributes](#customizing-the-validation-attributes) - - [Prepare Input For Validation](#prepare-input-for-validation) + - [Preparing Input For Validation](#preparing-input-for-validation) - [Manually Creating Validators](#manually-creating-validators) - [Automatic Redirection](#automatic-redirection) - [Named Error Bags](#named-error-bags) + - [Customizing The Error Messages](#manual-customizing-the-error-messages) - [After Validation Hook](#after-validation-hook) - [Working With Error Messages](#working-with-error-messages) - - [Custom Error Messages](#custom-error-messages) + - [Specifying Value Replacements In Language Files](#specifying-value-replacements-in-language-files) - [Available Validation Rules](#available-validation-rules) - [Conditionally Adding Rules](#conditionally-adding-rules) - [Validating Arrays](#validating-arrays) @@ -186,10 +186,17 @@ So, in our example, the user will be redirected to our controller's `create` met <!-- Create Post Form --> ``` +<a name="quick-customizing-the-error-messages"></a> +#### Customizing The Error Messages + +Laravel's built-in validation rules each have an error message that is located in your application's `resources/lang/en/validation.php` file. Within this file, you will find a translation entry for each validation rule. You are free to change or modify these messages based on the needs of your application. + +In addition, you may copy this file to another translation language directory to translate the messages for your application's language. To learn more about Laravel localization, check out the complete [localization documentation](/docs/{{version}}/localization). + <a name="quick-xhr-requests-and-validation"></a> #### XHR Requests & Validation -In this example, we used a traditional form to send data to the application. However, many applications receive XHR requests from a JavaScript powered frontend. When using the `validate` method during an XHR request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a `422` HTTP status code. +In this example, we used a traditional form to send data to the application. However, many applications receive XHR requests from a JavaScript powered frontend. When using the `validate` method during an XHR request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a 422 HTTP status code. <a name="the-at-error-directive"></a> #### The `@error` Directive @@ -227,11 +234,13 @@ In this example, we are specifying that the `publish_at` field may be either `nu <a name="creating-form-requests"></a> ### Creating Form Requests -For more complex validation scenarios, you may wish to create a "form request". Form requests are custom request classes that contain validation logic. To create a form request class, use the `make:request` Artisan CLI command: +For more complex validation scenarios, you may wish to create a "form request". Form requests are custom request classes that encapsulate their own validation and authorization logic. To create a form request class, you may use the `make:request` Artisan CLI command: + + php artisan make:request StorePostRequest - php artisan make:request StoreBlogPost +The generated form request class will be placed in the `app/Http/Requests` directory. If this directory does not exist, it will be created when you run the `make:request` command. Each form request generated by Laravel has two methods: `authorize` and `rules`. -The generated class will be placed in the `app/Http/Requests` directory. If this directory does not exist, it will be created when you run the `make:request` command. Let's add a few validation rules to the `rules` method: +As you might have guessed, the `authorize` method is responsible for determining if the currently authenticated user can perform the action represented by the request, while the `rules` method returns the validation rules that should apply to the request's data: /** * Get the validation rules that apply to the request. @@ -246,17 +255,17 @@ The generated class will be placed in the `app/Http/Requests` directory. If this ]; } -> {tip} You may type-hint any dependencies you need within the `rules` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). +> {tip} You may type-hint any dependencies you require within the `rules` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). So, how are the validation rules evaluated? All you need to do is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic: /** - * Store the incoming blog post. + * Store a new blog post. * - * @param StoreBlogPost $request - * @return Response + * @param \App\Http\Requests\StorePostRequest $request + * @return Illuminate\Http\Response */ - public function store(StoreBlogPost $request) + public function store(StorePostRequest $request) { // The incoming request is valid... @@ -264,12 +273,12 @@ So, how are the validation rules evaluated? All you need to do is type-hint the $validated = $request->validated(); } -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, an HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors. +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 XHR request, an HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors. <a name="adding-after-hooks-to-form-requests"></a> #### 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: +If you would like to add an "after" validation 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. @@ -289,7 +298,9 @@ If you would like to add an "after" hook to a form request, you may use the `wit <a name="authorizing-form-requests"></a> ### 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, you may determine if a user actually owns a blog comment they are attempting to update: +The form request class also contains an `authorize` method. Within this method, you may determine 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. Most likely, you will interact with your [authorization gates and policies](/docs/{{version}}/authorization) within this method: + + use App\Models\Comment; /** * Determine if the user is authorized to make this request. @@ -305,11 +316,11 @@ The form request class also contains an `authorize` method. Within this method, Since all form requests extend the base Laravel request class, we may use the `user` method to access the currently authenticated user. Also note the call to the `route` method in the example above. This method grants you access to the URI parameters defined on the route being called, such as the `{comment}` parameter in the example below: - Route::post('comment/{comment}'); + Route::post('/comment/{comment}'); If the `authorize` method returns `false`, an HTTP response with a 403 status code will automatically be returned and your controller method will not execute. -If you plan to have authorization logic in another part of your application, return `true` from the `authorize` method: +If you plan to handle authorization logic for the request in another part of your application, you may simply return `true` from the `authorize` method: /** * Determine if the user is authorized to make this request. @@ -342,9 +353,9 @@ You may customize the error messages used by the form request by overriding the } <a name="customizing-the-validation-attributes"></a> -### Customizing The Validation Attributes +#### Customizing The Validation Attributes -If you would like the `:attribute` portion of your validation message to be replaced with a custom attribute name, you may specify the custom names by overriding the `attributes` method. This method should return an array of attribute / name pairs: +Many of Laravel's built-in validation rule error messages contain an `:attribute` placeholder. If you would like the `:attribute` placeholder of your validation message to be replaced with a custom attribute name, you may specify the custom names by overriding the `attributes` method. This method should return an array of attribute / name pairs: /** * Get custom attributes for validator errors. @@ -358,10 +369,10 @@ If you would like the `:attribute` portion of your validation message to be repl ]; } -<a name="prepare-input-for-validation"></a> -### Prepare Input For Validation +<a name="preparing-input-for-validation"></a> +### Preparing Input For Validation -If you need to sanitize any data from the request before you apply your validation rules, you can use the `prepareForValidation` method: +If you need to prepare or sanitize any data from the request before you apply your validation rules, you may use the `prepareForValidation` method: use Illuminate\Support\Str; @@ -417,12 +428,12 @@ If you do not want to use the `validate` method on the request, you may create a The first argument passed to the `make` method is the data under validation. The second argument is an array of the validation rules that should be applied to the data. -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`. +After determining whether 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`. <a name="automatic-redirection"></a> ### Automatic Redirection -If you would like to create a validator instance manually but still take advantage of the automatic redirection offered by the request's `validate` method, you may call the `validate` method on an existing validator instance. If validation fails, the user will automatically be redirected or, in the case of an AJAX request, a JSON response will be returned: +If you would like to create a validator instance manually but still take advantage of the automatic redirection offered by the HTTP request's `validate` method, you may call the `validate` method on an existing validator instance. If validation fails, the user will automatically be redirected or, in the case of an XHR request, a JSON response will be returned: Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', @@ -439,7 +450,7 @@ You may use the `validateWithBag` method to store the error messages in a [named <a name="named-error-bags"></a> ### Named Error Bags -If you have multiple forms on a single page, you may wish to name the `MessageBag` of errors, allowing you to retrieve the error messages for a specific form. Pass a name as the second argument to `withErrors`: +If you have multiple forms on a single page, you may wish to name the `MessageBag` containing the validation errors, allowing you to retrieve the error messages for a specific form. To achieve this, pass a name as the second argument to `withErrors`: return redirect('register') ->withErrors($validator, 'login'); @@ -448,16 +459,71 @@ You may then access the named `MessageBag` instance from the `$errors` variable: {{ $errors->login->first('email') }} +<a name="manual-customizing-the-error-messages"></a> +### Customizing The Error Messages + +If needed, you may use custom error messages for a validator instance instead of the default error messages. There are several ways to specify custom messages. First, you may pass the custom messages as the third argument to the `Validator::make` method: + + $validator = Validator::make($input, $rules, [ + 'required' => 'The :attribute field is required.', + ]); + +In this example, the `:attribute` placeholder will be replaced by the actual name of the field under validation. You may also utilize other placeholders in validation messages. For example: + + $messages = [ + 'same' => 'The :attribute and :other must match.', + 'size' => 'The :attribute must be exactly :size.', + 'between' => 'The :attribute value :input is not between :min - :max.', + 'in' => 'The :attribute must be one of the following types: :values', + ]; + +<a name="specifying-a-custom-message-for-a-given-attribute"></a> +#### Specifying A Custom Message For A Given Attribute + +Sometimes you may wish to specify a custom error message only for a specific attribute. You may do so using "dot" notation. Specify the attribute's name first, followed by the rule: + + $messages = [ + 'email.required' => 'We need to know your e-mail address!', + ]; + +<a name="localization"></a> +#### Specifying Custom Messages In Language Files + +In most cases, you will probably specify your custom messages in a language file instead of passing them directly to a validator instance. To do so, add your messages to the `custom` array in the `resources/lang/xx/validation.php` language file: + + 'custom' => [ + 'email' => [ + 'required' => 'We need to know your e-mail address!', + ], + ], + +<a name="specifying-custom-attribute-values"></a> +#### Specifying Custom Attribute Values + +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', + ], + +You may also pass the custom attributes as the fourth argument to the `Validator::make` method: + + $validator = Validator::make($input, $rules, $messages, [ + 'email' => 'email address', + ]); + <a name="after-validation-hook"></a> ### After Validation Hook -The validator also allows you to attach callbacks to be run after validation is completed. This allows you to easily perform further validation and even add more error messages to the message collection. To get started, use the `after` method on a validator instance: +You may also attach callbacks to be run after validation is completed. This allows you to easily perform further validation and even add more error messages to the message collection. To get started, call the `after` method on a validator instance: $validator = Validator::make(...); $validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { - $validator->errors()->add('field', 'Something is wrong with this field!'); + $validator->errors()->add( + 'field', 'Something is wrong with this field!' + ); } }); @@ -512,69 +578,12 @@ The `has` method may be used to determine if any error messages exist for a give // } -<a name="custom-error-messages"></a> -### Custom Error Messages - -If needed, you may use custom error messages for validation instead of the defaults. There are several ways to specify custom messages. First, you may pass the custom messages as the third argument to the `Validator::make` method: - - $messages = [ - 'required' => 'The :attribute field is required.', - ]; - - $validator = Validator::make($input, $rules, $messages); - -In this example, the `:attribute` placeholder will be replaced by the actual name of the field under validation. You may also utilize other placeholders in validation messages. For example: - - $messages = [ - 'same' => 'The :attribute and :other must match.', - 'size' => 'The :attribute must be exactly :size.', - 'between' => 'The :attribute value :input is not between :min - :max.', - 'in' => 'The :attribute must be one of the following types: :values', - ]; - -<a name="specifying-a-custom-message-for-a-given-attribute"></a> -#### Specifying A Custom Message For A Given Attribute - -Sometimes you may wish to specify a custom error message only for a specific field. You may do so using "dot" notation. Specify the attribute's name first, followed by the rule: - - $messages = [ - 'email.required' => 'We need to know your e-mail address!', - ]; - -<a name="localization"></a> -#### Specifying Custom Messages In Language Files - -In most cases, you will probably specify your custom messages in a language file instead of passing them directly to the `Validator`. To do so, add your messages to `custom` array in the `resources/lang/xx/validation.php` language file. - - 'custom' => [ - 'email' => [ - 'required' => 'We need to know your e-mail address!', - ], - ], - -<a name="specifying-custom-attribute-values"></a> -#### Specifying Custom Attribute Values - -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', - ], +<a name="specifying-value-replacements-in-language-files"></a> +### Specifying Value Replacements In Language Files -You may also pass the custom attributes as the fourth argument to the `Validator::make` method: +Some of Laravel's built-in validation rule error messages contain a `:value` placeholder that is replaced with the current value of the request attribute. However, you may occasionally need the `:value` portion of your validation message to be replaced with a custom representation of the value. For example, consider the following rule that specifies that a credit card number is required if the `payment_type` has a value of `cc`: - $customAttributes = [ - 'email' => 'email address', - ]; - - $validator = Validator::make($input, $rules, $messages, $customAttributes); - -<a name="specifying-custom-values-in-language-files"></a> -#### Specifying Custom Values In Language Files - -Sometimes you may need the `:value` portion of your validation message to be replaced with a custom representation of the value. For example, consider the following rule that specifies that a credit card number is required if the `payment_type` has a value of `cc`: - - $request->validate([ + Validator::make($request->all(), [ 'credit_card_number' => 'required_if:payment_type,cc' ]); @@ -582,7 +591,7 @@ If this validation rule fails, it will produce the following error message: The credit card number field is required when payment type is cc. -Instead of displaying `cc` as the payment type value, you may specify a custom value representation in your `validation` language file by defining a `values` array: +Instead of displaying `cc` as the payment type value, you may specify a more user-friendly value representation in your `resources/lang/xx/validation.php` language file by defining a `values` array: 'values' => [ 'payment_type' => [ @@ -590,7 +599,7 @@ Instead of displaying `cc` as the payment type value, you may specify a custom v ], ], -Now if the validation rule fails it will produce the following message: +After defining this value, the validation rule will produce the following error message: The credit card number field is required when payment type is credit card. From c58cd51b9b809d039937f33b1be0b7bcb47f4116 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 14:52:16 -0600 Subject: [PATCH 080/274] wip --- validation.md | 67 +++++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/validation.md b/validation.md index f1ca184fc6..dc0914d67c 100644 --- a/validation.md +++ b/validation.md @@ -483,7 +483,7 @@ In this example, the `:attribute` placeholder will be replaced by the actual nam Sometimes you may wish to specify a custom error message only for a specific attribute. You may do so using "dot" notation. Specify the attribute's name first, followed by the rule: $messages = [ - 'email.required' => 'We need to know your e-mail address!', + 'email.required' => 'We need to know your email address!', ]; <a name="localization"></a> @@ -493,7 +493,7 @@ In most cases, you will probably specify your custom messages in a language file 'custom' => [ 'email' => [ - 'required' => 'We need to know your e-mail address!', + 'required' => 'We need to know your email address!', ], ], @@ -694,7 +694,7 @@ Below is a list of all available validation rules and their function: <a name="rule-accepted"></a> #### accepted -The field under validation must be _yes_, _on_, _1_, or _true_. This is useful for validating "Terms of Service" acceptance. +The field under validation must be `"yes"`, `"on"`, `1`, or `true`. This is useful for validating "Terms of Service" acceptance or similar fields. <a name="rule-active-url"></a> #### active_url @@ -704,7 +704,7 @@ The field under validation must have a valid A or AAAA record according to the ` <a name="rule-after"></a> #### after:_date_ -The field under validation must be a value after a given date. The dates will be passed into the `strtotime` PHP function: +The field under validation must be a value after a given date. The dates will be passed into the `strtotime` PHP function in order to be converted to a valid `DateTime` instance: 'start_date' => 'required|date|after:tomorrow' @@ -745,12 +745,12 @@ Stop running validation rules after the first validation failure. <a name="rule-before"></a> #### before:_date_ -The field under validation must be a value preceding the given date. The dates will be passed into the PHP `strtotime` function. In addition, like the [`after`](#rule-after) rule, the name of another field under validation may be supplied as the value of `date`. +The field under validation must be a value preceding the given date. The dates will be passed into the PHP `strtotime` function in order to be converted into a valid `DateTime` instance. In addition, like the [`after`](#rule-after) rule, the name of another field under validation may be supplied as the value of `date`. <a name="rule-before-or-equal"></a> #### 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. In addition, like the [`after`](#rule-after) rule, the name of another field under validation may be supplied as the value of `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 in order to be converted into a valid `DateTime` instance. In addition, like the [`after`](#rule-after) rule, the name of another field under validation may be supplied as the value of `date`. <a name="rule-between"></a> #### between:_min_,_max_ @@ -765,7 +765,7 @@ The field under validation must be able to be cast as a boolean. Accepted input <a name="rule-confirmed"></a> #### confirmed -The field under validation must have a matching field of `foo_confirmation`. For example, if the field under validation is `password`, a matching `password_confirmation` field must be present in the input. +The field under validation must have a matching field of `{field}_confirmation`. For example, if the field under validation is `password`, a matching `password_confirmation` field must be present in the input. <a name="rule-date"></a> #### date @@ -775,7 +775,7 @@ The field under validation must be a valid, non-relative date according to the ` <a name="rule-date-equals"></a> #### date_equals:_date_ -The field under validation must be equal to the given date. The dates will be passed into the PHP `strtotime` function. +The field under validation must be equal to the given date. The dates will be passed into the PHP `strtotime` function in order to be converted into a valid `DateTime` instance. <a name="rule-date-format"></a> #### date_format:_format_ @@ -806,12 +806,13 @@ The file under validation must be an image meeting the dimension constraints as Available constraints are: _min\_width_, _max\_width_, _min\_height_, _max\_height_, _width_, _height_, _ratio_. -A _ratio_ constraint should be represented as width divided by height. This can be specified either by a statement like `3/2` or a float like `1.5`: +A _ratio_ constraint should be represented as width divided by height. This can be specified either by a fraction like `3/2` or a float like `1.5`: '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\Support\Facades\Validator; use Illuminate\Validation\Rule; Validator::make($data, [ @@ -831,7 +832,7 @@ When working with arrays, the field under validation must not have any duplicate <a name="rule-email"></a> #### email -The field under validation must be formatted as an e-mail address. Under the hood, this validation rule makes use of the [`egulias/email-validator`](https://github.com/egulias/EmailValidator) package for validating the email address. By default the `RFCValidation` validator is applied, but you can apply other validation styles as well: +The field under validation must be formatted as an email address. This validation rule utilizes the [`egulias/email-validator`](https://github.com/egulias/EmailValidator) package for validating the email address. By default the `RFCValidation` validator is applied, but you can apply other validation styles as well: 'email' => 'email:rfc,dns' @@ -845,7 +846,9 @@ The example above will apply the `RFCValidation` and `DNSCheckValidation` valida - `filter`: `FilterEmailValidation` </div> -The `filter` validator, which uses PHP's `filter_var` function under the hood, ships with Laravel and is Laravel's pre-5.8 behavior. The `dns` and `spoof` validators require the PHP `intl` extension. +The `filter` validator, which uses PHP's `filter_var` function, ships with Laravel and was Laravel's default email validation behavior prior to Laravel version 5.8. + +> {note} The `dns` and `spoof` validators require the PHP `intl` extension. <a name="rule-ends-with"></a> #### ends_with:_foo_,_bar_,... @@ -865,21 +868,23 @@ The field under validation will be excluded from the request data returned by th <a name="rule-exists"></a> #### exists:_table_,_column_ -The field under validation must exist on a given database table. +The field under validation must exist in a given database table. <a name="basic-usage-of-exists-rule"></a> #### Basic Usage Of Exists Rule 'state' => 'exists:states' -If the `column` option is not specified, the field name will be used. +If the `column` option is not specified, the field name will be used. So, in this case, the rule will validate that the `states` database table contains a record with a `state` column value matching the request's `state` attribute value. <a name="specifying-a-custom-column-name"></a> #### Specifying A Custom Column Name +You may explicitly specify the database column name that should be used by the validation rule by placing it after the database table name: + 'state' => 'exists:states,abbreviation' -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: +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: 'email' => 'exists:connection.staff,email' @@ -889,6 +894,7 @@ Instead of specifying the table name directly, you may specify the Eloquent mode 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: + use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; Validator::make($data, [ @@ -923,13 +929,14 @@ The field under validation must be greater than or equal to the given _field_. T <a name="rule-image"></a> #### image -The file under validation must be an image (jpeg, png, bmp, gif, svg, or webp) +The file under validation must be an image (jpeg, png, bmp, gif, svg, or webp). <a name="rule-in"></a> #### in:_foo_,_bar_,... 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\Support\Facades\Validator; use Illuminate\Validation\Rule; Validator::make($data, [ @@ -993,7 +1000,7 @@ The file under validation must match one of the given MIME types: 'video' => 'mimetypes:video/avi,video/mpeg,video/quicktime' -To determine the MIME type of the uploaded file, the file's contents will be read and the framework will attempt to guess the MIME type, which may be different from the client provided MIME type. +To determine the MIME type of the uploaded file, the file's contents will be read and the framework will attempt to guess the MIME type, which may be different from the client's provided MIME type. <a name="rule-mimes"></a> #### mimes:_foo_,_bar_,... @@ -1005,9 +1012,9 @@ The file under validation must have a MIME type corresponding to one of the list 'photo' => 'mimes:jpeg,bmp,png' -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. +Even though you only need to specify the extensions, this rule actually validates 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: -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) +[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) <a name="rule-min"></a> #### min:_value_ @@ -1040,12 +1047,12 @@ The field under validation must not match the given regular expression. Internally, this rule uses the PHP `preg_match` function. The pattern specified should obey the same formatting required by `preg_match` and thus also include valid delimiters. For example: `'email' => 'not_regex:/^.+$/i'`. -**Note:** When using the `regex` / `not_regex` patterns, it may be necessary to specify rules in an array instead of using pipe delimiters, especially if the regular expression contains a pipe character. +> {note} When using the `regex` / `not_regex` patterns, it may be necessary to specify your validation rules using an array instead of using `|` delimiters, especially if the regular expression contains a `|` character. <a name="rule-nullable"></a> #### nullable -The field under validation may be `null`. This is particularly useful when validating primitive such as strings and integers that can contain `null` values. +The field under validation may be `null`. <a name="rule-numeric"></a> #### numeric @@ -1055,7 +1062,7 @@ The field under validation must be numeric. <a name="rule-password"></a> #### password -The field under validation must match the authenticated user's password. You may specify an authentication guard using the rule's first parameter: +The field under validation must match the authenticated user's password. You may specify an [authentication guard](/docs/{{version}}/authentication) using the rule's first parameter: 'password' => 'password:api' @@ -1071,7 +1078,7 @@ The field under validation must match the given regular expression. Internally, this rule uses the PHP `preg_match` function. The pattern specified should obey the same formatting required by `preg_match` and thus also include valid delimiters. For example: `'email' => 'regex:/^.+@.+$/i'`. -**Note:** When using the `regex` / `not_regex` patterns, it may be necessary to specify rules in an array instead of using pipe delimiters, especially if the regular expression contains a pipe character. +> {note} When using the `regex` / `not_regex` patterns, it may be necessary to specify rules in an array instead of using `|` delimiters, especially if the regular expression contains a `|` character. <a name="rule-required"></a> #### required @@ -1094,6 +1101,7 @@ The field under validation must be present and not empty if the _anotherfield_ f If you would like to construct a more complex condition for the `required_if` rule, you may use the `Rule::requiredIf` method. This methods accepts a boolean or a closure. When passed a closure, the closure should return `true` or `false` to indicate if the field under validation is required: + use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; Validator::make($request->all(), [ @@ -1179,22 +1187,23 @@ Instead of specifying the table name directly, you may specify the Eloquent mode 'email' => 'unique:App\Models\User,email_address' -The `column` option may be used to specify the field's corresponding database column. If the `column` option is not specified, the field name will be used. +The `column` option may be used to specify the field's corresponding database column. If the `column` option is not specified, the name of the field under validation will be used. 'email' => 'unique:users,email_address' **Custom Database Connection** -Occasionally, you may need to set a custom connection for database queries made by the Validator. As seen above, setting `unique:users` as a validation rule will use the default database connection to query the database. To override this, specify the connection and the table name using "dot" syntax: +Occasionally, you may need to set a custom connection for database queries made by the Validator. To accomplish this, you may prepend the connection name to the table name: 'email' => 'unique:connection.users,email_address' **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. You will probably 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. +Sometimes, you may wish to ignore a given ID during unique validation. For example, consider an "update profile" screen that includes the user's name, email address, and location. You will probably want to verify that the email address is unique. However, if the user only changes the name field and not the email field, you do not want a validation error to be thrown because the user is already the owner of the email address in question. 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: + use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; Validator::make($data, [ @@ -1206,7 +1215,7 @@ To instruct the validator to ignore the user's ID, we'll use the `Rule` class to > {note} You should never pass any user controlled request input into the `ignore` method. Instead, you should only pass a system generated unique ID such as an auto-incrementing ID or UUID from an Eloquent model instance. Otherwise, your application will be vulnerable to an SQL injection attack. -Instead of passing the model key's value to the `ignore` method, you may pass the entire model instance. Laravel will automatically extract the key from the model: +Instead of passing the model key's value to the `ignore` method, you may also pass the entire model instance. Laravel will automatically extract the key from the model: Rule::unique('users')->ignore($user) @@ -1220,7 +1229,7 @@ By default, the `unique` rule will check the uniqueness of the column matching t **Adding Additional Where Clauses:** -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`: +You may specify additional query conditions by customizing the query using the `where` method. For example, let's add a query condition that scopes the query to only search records that have an `account_id` column value of `1`: 'email' => Rule::unique('users')->where(function ($query) { return $query->where('account_id', 1); @@ -1304,7 +1313,7 @@ Validating array based form input fields doesn't have to be a pain. You may use 'photos.profile' => 'required|image', ]); -You may also validate each element of an array. For example, to validate that each e-mail in a given array input field is unique, you may do the following: +You may also validate each element of an array. For example, to validate that each email in a given array input field is unique, you may do the following: $validator = Validator::make($request->all(), [ 'person.*.email' => 'email|unique:users', @@ -1315,7 +1324,7 @@ Likewise, you may use the `*` character when specifying your validation messages 'custom' => [ 'person.*.email' => [ - 'unique' => 'Each person must have a unique e-mail address', + 'unique' => 'Each person must have a unique email address', ] ], From 497eb3e0d579377d0e75bd220d004ba9d49c7563 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 14:53:15 -0600 Subject: [PATCH 081/274] wip --- validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation.md b/validation.md index dc0914d67c..1b1316ee84 100644 --- a/validation.md +++ b/validation.md @@ -1191,7 +1191,7 @@ The `column` option may be used to specify the field's corresponding database co 'email' => 'unique:users,email_address' -**Custom Database Connection** +**Specifying A Custom Database Connection** Occasionally, you may need to set a custom connection for database queries made by the Validator. To accomplish this, you may prepend the connection name to the table name: From 5b2efbe30039d93d5102dbf2e38e9b3712f1d940 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 15:11:33 -0600 Subject: [PATCH 082/274] wip --- validation.md | 79 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/validation.md b/validation.md index 1b1316ee84..708a624fa9 100644 --- a/validation.md +++ b/validation.md @@ -18,7 +18,9 @@ - [Customizing The Error Messages](#manual-customizing-the-error-messages) - [After Validation Hook](#after-validation-hook) - [Working With Error Messages](#working-with-error-messages) - - [Specifying Value Replacements In Language Files](#specifying-value-replacements-in-language-files) + - [Specifying Custom Messages In Language Files](#specifying-custom-messages-in-language-files) + - [Specifying Attributes In Language Files](#specifying-attribute-in-language-files) + - [Specifying Values In Language Files](#specifying-values-replacements-in-language-files) - [Available Validation Rules](#available-validation-rules) - [Conditionally Adding Rules](#conditionally-adding-rules) - [Validating Arrays](#validating-arrays) @@ -464,7 +466,7 @@ You may then access the named `MessageBag` instance from the `$errors` variable: If needed, you may use custom error messages for a validator instance instead of the default error messages. There are several ways to specify custom messages. First, you may pass the custom messages as the third argument to the `Validator::make` method: - $validator = Validator::make($input, $rules, [ + $validator = Validator::make($input, $rules, $messages = [ 'required' => 'The :attribute field is required.', ]); @@ -486,27 +488,10 @@ Sometimes you may wish to specify a custom error message only for a specific att 'email.required' => 'We need to know your email address!', ]; -<a name="localization"></a> -#### Specifying Custom Messages In Language Files - -In most cases, you will probably specify your custom messages in a language file instead of passing them directly to a validator instance. To do so, add your messages to the `custom` array in the `resources/lang/xx/validation.php` language file: - - 'custom' => [ - 'email' => [ - 'required' => 'We need to know your email address!', - ], - ], - <a name="specifying-custom-attribute-values"></a> #### Specifying Custom Attribute Values -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', - ], - -You may also pass the custom attributes as the fourth argument to the `Validator::make` method: +Many of Laravel's built-in error messages include an `:attribute:` placeholder that is replaced with the name of the field or attribute under validation. To customize the values used to replace these placeholders for specific fields, you may pass an array of custom attributes as the fourth argument to the `Validator::make` method: $validator = Validator::make($input, $rules, $messages, [ 'email' => 'email address', @@ -578,8 +563,36 @@ The `has` method may be used to determine if any error messages exist for a give // } -<a name="specifying-value-replacements-in-language-files"></a> -### Specifying Value Replacements In Language Files +<a name="specifying-custom-messages-in-language-files"></a> +### Specifying Custom Messages In Language Files + +Laravel's built-in validation rules each have an error message that is located in your application's `resources/lang/en/validation.php` file. Within this file, you will find a translation entry for each validation rule. You are free to change or modify these messages based on the needs of your application. + +In addition, you may copy this file to another translation language directory to translate the messages for your application's language. To learn more about Laravel localization, check out the complete [localization documentation](/docs/{{version}}/localization). + +<a name="custom-messages-for-specific-attributes"></a> +#### Custom Messages For Specific Attributes + +You may customize the error messages used for specified attribute and rule combinations within your application's validation language files. To do so, add your message customizations to the `custom` array of your application's `resources/lang/xx/validation.php` language file: + + 'custom' => [ + 'email' => [ + 'required' => 'We need to know your email address!', + 'max' => 'Your email address is too long!' + ], + ], + +<a name="specifying-attribute-in-language-files"></a> +### Specifying Attributes In Language Files + +Many of Laravel's built-in error messages include an `:attribute:` placeholder that is replaced with the name of the field or attribute under validation. If you would like the `:attribute` portion of your validation message to be replaced with a custom value, you may specify the custom attribute name in the `attributes` array of your `resources/lang/xx/validation.php` language file: + + 'attributes' => [ + 'email' => 'email address', + ], + +<a name="specifying-values-in-language-files"></a> +### Specifying Values In Language Files Some of Laravel's built-in validation rule error messages contain a `:value` placeholder that is replaced with the current value of the request attribute. However, you may occasionally need the `:value` portion of your validation message to be replaced with a custom representation of the value. For example, consider the following rule that specifies that a credit card number is required if the `payment_type` has a value of `cc`: @@ -1253,7 +1266,9 @@ The field under validation must be a valid RFC 4122 (version 1, 3, 4, or 5) univ You may occasionally wish to not validate a given field if another field has a given value. You may accomplish this using the `exclude_if` validation rule. In this example, the `appointment_date` and `doctor_name` fields will not be validated if the `has_appointment` field has a value of `false`: - $v = Validator::make($data, [ + use Illuminate\Support\Facades\Validator; + + $validator = Validator::make($data, [ 'has_appointment' => 'required|bool', 'appointment_date' => 'exclude_if:has_appointment,false|required|date', 'doctor_name' => 'exclude_if:has_appointment,false|required|string', @@ -1261,7 +1276,7 @@ You may occasionally wish to not validate a given field if another field has a g Alternatively, you may use the `exclude_unless` rule to not validate a given field unless another field has a given value: - $v = Validator::make($data, [ + $validator = Validator::make($data, [ 'has_appointment' => 'required|bool', 'appointment_date' => 'exclude_unless:has_appointment,true|required|date', 'doctor_name' => 'exclude_unless:has_appointment,true|required|string', @@ -1270,9 +1285,9 @@ Alternatively, you may use the `exclude_unless` rule to not validate a given fie <a name="validating-when-present"></a> #### Validating When Present -In some situations, you may wish to run validation checks against a field **only** if that field is present in the input array. To quickly accomplish this, add the `sometimes` rule to your rule list: +In some situations, you may wish to run validation checks against a field **only** if that field is present in the data being validated. To quickly accomplish this, add the `sometimes` rule to your rule list: - $v = Validator::make($data, [ + $v = Validator::make($request->all(), [ 'email' => 'sometimes|required|email', ]); @@ -1285,12 +1300,14 @@ In the example above, the `email` field will only be validated if it is present Sometimes you may wish to add validation rules based on more complex conditional logic. For example, you may wish to require a given field only if another field has a greater value than 100. Or, you may need two fields to have a given value only when another field is present. Adding these validation rules doesn't have to be a pain. First, create a `Validator` instance with your _static rules_ that never change: - $v = Validator::make($data, [ + use Illuminate\Support\Facades\Validator; + + $validator = Validator::make($request->all(), [ 'email' => 'required|email', 'games' => 'required|numeric', ]); -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. +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 games. To conditionally add this requirement, we can use the `sometimes` method on the `Validator` instance. $v->sometimes('reason', 'required|max:500', function ($input) { return $input->games >= 100; @@ -1302,13 +1319,15 @@ The first argument passed to the `sometimes` method is the name of the field we return $input->games >= 100; }); -> {tip} The `$input` parameter passed to your closure will be an instance of `Illuminate\Support\Fluent` and may be used to access your input and files. +> {tip} The `$input` parameter passed to your closure will be an instance of `Illuminate\Support\Fluent` and may be used to access your input and files under validation. <a name="validating-arrays"></a> ## Validating Arrays Validating array based form input fields doesn't have to be a pain. You may use "dot notation" to validate attributes within an array. For example, if the incoming HTTP request contains a `photos[profile]` field, you may validate it like so: + use Illuminate\Support\Facades\Validator; + $validator = Validator::make($request->all(), [ 'photos.profile' => 'required|image', ]); @@ -1320,7 +1339,7 @@ You may also validate each element of an array. For example, to validate that ea 'person.*.first_name' => 'required_with:person.*.last_name', ]); -Likewise, you may use the `*` character when specifying your validation messages in your language files, making it a breeze to use a single validation message for array based fields: +Likewise, you may use the `*` character when specifying [custom validation messages in your language files](#custom-messages-for-specific-attributes), making it a breeze to use a single validation message for array based fields: 'custom' => [ 'person.*.email' => [ From f7e5faf8cf1e4170e63bd2bc762f0db2d91ff8a7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 15:17:51 -0600 Subject: [PATCH 083/274] wip --- validation.md | 96 ++++++--------------------------------------------- 1 file changed, 10 insertions(+), 86 deletions(-) diff --git a/validation.md b/validation.md index 708a624fa9..bc3304bd49 100644 --- a/validation.md +++ b/validation.md @@ -27,8 +27,7 @@ - [Custom Validation Rules](#custom-validation-rules) - [Using Rule Objects](#using-rule-objects) - [Using Closures](#using-closures) - - [Using Extensions](#using-extensions) - - [Implicit Extensions](#implicit-extensions) + - [Implicit Rules](#implicit-rules) <a name="introduction"></a> ## Introduction @@ -1353,7 +1352,7 @@ Likewise, you may use the `*` character when specifying [custom validation messa <a name="using-rule-objects"></a> ### Using Rule Objects -Laravel provides a variety of helpful validation rules; however, you may wish to specify some of your own. One method of registering custom validation rules is using rule objects. To generate a new rule object, you may use the `make:rule` Artisan command. Let's use this command to generate a rule that verifies a string is uppercase. Laravel will place the new rule in the `app/Rules` directory: +Laravel provides a variety of helpful validation rules; however, you may wish to specify some of your own. One method of registering custom validation rules is using rule objects. To generate a new rule object, you may use the `make:rule` Artisan command. Let's use this command to generate a rule that verifies a string is uppercase. Laravel will place the new rule in the `app/Rules` directory. If this directory does not exist, Laravel will create it when you execute the Artisan command to create your rule: php artisan make:rule Uppercase @@ -1415,108 +1414,33 @@ Once the rule has been defined, you may attach it to a validator by passing an i If you only need the functionality of a custom rule once throughout your application, you may use a closure instead of a rule object. The closure receives the attribute's name, the attribute's value, and a `$fail` callback that should be called if validation fails: + use Illuminate\Support\Facades\Validator; + $validator = Validator::make($request->all(), [ 'title' => [ 'required', 'max:255', function ($attribute, $value, $fail) { if ($value === 'foo') { - $fail($attribute.' is invalid.'); + $fail('The '.$attribute.' is invalid.'); } }, ], ]); -<a name="using-extensions"></a> -### Using Extensions - -Another method of registering custom validation rules is using the `extend` method on the `Validator` [facade](/docs/{{version}}/facades). Let's use this method within a [service provider](/docs/{{version}}/providers) to register a custom validation rule: +<a name="implicit-rules"></a> +### Implicit Rules - <?php - - namespace App\Providers; +By default, when an attribute being validated is not present or contains an empty string, normal validation rules, including custom rules, are not run. For example, the [`unique`](#rule-unique) rule will not be run against an empty string: - use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Validator; - class AppServiceProvider extends ServiceProvider - { - /** - * Register any application services. - * - * @return void - */ - public function register() - { - // - } - - /** - * Bootstrap any application services. - * - * @return void - */ - public function boot() - { - Validator::extend('foo', function ($attribute, $value, $parameters, $validator) { - return $value == 'foo'; - }); - } - } - -The custom validator closure receives four arguments: the name of the `$attribute` being validated, the `$value` of the attribute, an array of `$parameters` passed to the rule, and the `Validator` instance. - -You may also pass a class and method to the `extend` method instead of a closure: - - Validator::extend('foo', 'FooValidator@validate'); - -<a name="defining-the-error-message"></a> -#### Defining The Error Message - -You will also need to define an error message for your custom rule. You can do so either using an inline custom message array or by adding an entry in the validation language file. This message should be placed in the first level of the array, not within the `custom` array, which is only for attribute-specific error messages: - - "foo" => "Your input was invalid!", - - "accepted" => "The :attribute must be accepted.", - - // The rest of the validation error messages... - -When creating a custom validation rule, you may sometimes need to define custom placeholder replacements for error messages. You may do so by creating a custom Validator as described above then making a call to the `replacer` method on the `Validator` facade. You may do this within the `boot` method of a [service provider](/docs/{{version}}/providers): - - /** - * Bootstrap any application services. - * - * @return void - */ - public function boot() - { - Validator::extend(...); - - Validator::replacer('foo', function ($message, $attribute, $rule, $parameters) { - return str_replace(...); - }); - } - -<a name="implicit-extensions"></a> -### Implicit Extensions - -By default, when an attribute being validated is not present or contains an empty string, normal validation rules, including custom extensions, are not run. For example, the [`unique`](#rule-unique) rule will not be run against an empty string: - $rules = ['name' => 'unique:users,name']; $input = ['name' => '']; Validator::make($input, $rules)->passes(); // true -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) { - return $value == 'foo'; - }); - -> {note} An "implicit" extension only _implies_ that the attribute is required. Whether it actually invalidates a missing or empty attribute is up to you. - -<a name="implicit-rule-objects"></a> -#### Implicit Rule Objects +For a custom rule to run even when an attribute is empty, the rule must imply that the attribute is required. To create an "implicit" rule, implement the `Illuminate\Contracts\Validation\ImplicitRule` interface. This interface serves as a "marker interface" for the validator; therefore, it does not contain any additional methods you need to implement beyond the methods required by the typical `Rule` interface. -If you would like a rule object to run when an attribute is empty, you should implement the `Illuminate\Contracts\Validation\ImplicitRule` interface. This interface serves as a "marker interface" for the validator; therefore, it does not contain any methods you need to implement. +> {note} An "implicit" rule only _implies_ that the attribute is required. Whether it actually invalidates a missing or empty attribute is up to you. From 089118f8a79d295bf5cd40714e989852eabaab6e Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 16:07:14 -0600 Subject: [PATCH 084/274] wip --- errors.md | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/errors.md b/errors.md index 87c858d6bf..80a81db273 100644 --- a/errors.md +++ b/errors.md @@ -12,14 +12,14 @@ <a name="introduction"></a> ## Introduction -When you start a new Laravel project, error and exception handling is already configured for you. The `App\Exceptions\Handler` class is where all exceptions triggered by your application are logged and then rendered back to the user. We'll dive deeper into this class throughout this documentation. +When you start a new Laravel project, error and exception handling is already configured for you. The `App\Exceptions\Handler` class is where all exceptions thrown by your application are logged and then rendered to the user. We'll dive deeper into this class throughout this documentation. <a name="configuration"></a> ## Configuration The `debug` option in your `config/app.php` configuration file determines how much information about an error is actually displayed to the user. By default, this option is set to respect the value of the `APP_DEBUG` environment variable, which is stored in your `.env` file. -For local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the value is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** +During local development, you should set the `APP_DEBUG` environment variable to `true`. **In your production environment, this value should always be `false`. If the value is set to `true` in production, you risk exposing sensitive configuration values to your application's end users.** <a name="the-exception-handler"></a> ## The Exception Handler @@ -27,11 +27,11 @@ For local development, you should set the `APP_DEBUG` environment variable to `t <a name="reporting-exceptions"></a> ### Reporting Exceptions -All exceptions are handled by the `App\Exceptions\Handler` class. This class contains a `register` method where you may register custom exception reporter and renderer callbacks. We'll examine each of these concepts in detail. Exception reporting is used to log exceptions or send them to an external service like [Flare](https://flareapp.io), [Bugsnag](https://bugsnag.com) or [Sentry](https://github.com/getsentry/sentry-laravel). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. However, you are free to log exceptions however you wish. +All exceptions are handled by the `App\Exceptions\Handler` class. This class contains a `register` method where you may register custom exception reporting and rendering callbacks. We'll examine each of these concepts in detail. Exception reporting is used to log exceptions or send them to an external service like [Flare](https://flareapp.io), [Bugsnag](https://bugsnag.com) or [Sentry](https://github.com/getsentry/sentry-laravel). By default, exceptions will be logged based on your [logging](/docs/{{version}}/logging) configuration. 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 `reportable` method to register a closure that should be executed when an exception of a given type needs to be reported. Laravel will deduce what type of exception the closure reports by examining the type-hint of the closure: - use App\Exceptions\CustomException; + use App\Exceptions\InvalidOrderException; /** * Register the exception handling callbacks for the application. @@ -40,18 +40,18 @@ For example, if you need to report different types of exceptions in different wa */ public function register() { - $this->reportable(function (CustomException $e) { + $this->reportable(function (InvalidOrderException $e) { // }); } When you register a custom exception reporting callback using the `reportable` method, Laravel will still log the exception using the default logging configuration for the application. If you wish to stop the propagation of the exception to the default logging stack, you may use the `stop` method when defining your reporting callback or return `false` from the callback: - $this->reportable(function (CustomException $e) { + $this->reportable(function (InvalidOrderException $e) { // })->stop(); - $this->reportable(function (CustomException $e) { + $this->reportable(function (InvalidOrderException $e) { return false; }); @@ -77,7 +77,7 @@ If available, Laravel automatically adds the current user's ID to every exceptio <a name="the-report-helper"></a> #### The `report` Helper -Sometimes you may need to report an exception but continue handling the current request. The `report` helper function allows you to quickly report an exception using your exception handler without rendering an error page: +Sometimes you may need to report an exception but continue handling the current request. The `report` helper function allows you to quickly report an exception via the exception handler without rendering an error page to the user: public function isValid($value) { @@ -93,7 +93,7 @@ Sometimes you may need to report an exception but continue handling the current <a name="ignoring-exceptions-by-type"></a> #### Ignoring Exceptions By Type -The `$dontReport` property of the exception handler contains an array of exception types that will not be logged. For example, exceptions resulting from 404 errors, as well as several other types of errors, are not written to your log files. You may add other exception types to this array as needed: +The `$dontReport` property of the exception handler contains an array of exception types that will not be reported or logged. For example, exceptions resulting from 404 HTTP errors, as well as several other types of errors, are not written to your log files. You may add other exception types to this array as needed: /** * A list of the exception types that should not be reported. @@ -111,9 +111,11 @@ The `$dontReport` property of the exception handler contains an array of excepti <a name="rendering-exceptions"></a> ### Rendering Exceptions -By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this via the `renderable` method of your exception handler. Laravel will deduce what type of exception the closure renders by examining the type-hint of the closure: +By default, the Laravel exception handler will convert exceptions into an HTTP response for you. However, you are free to register a custom rendering closure for exceptions of a given type. You may accomplish this via the `renderable` method of your exception handler. - use App\Exceptions\CustomException; +The closure passed to the `renderable` method should return an instance of `Illuminate\Http\Response`, which may be generated via the `response` helper. Laravel will deduce what type of exception the closure renders by examining the type-hint of the closure: + + use App\Exceptions\InvalidOrderException; /** * Register the exception handling callbacks for the application. @@ -122,15 +124,15 @@ By default, the Laravel exception handler will convert exceptions into an HTTP r */ public function register() { - $this->renderable(function (CustomException $e, $request) { - return response()->view('errors.custom', [], 500); + $this->renderable(function (InvalidOrderException $e, $request) { + return response()->view('errors.invalid-order', [], 500); }); } <a name="renderable-exceptions"></a> ### Reportable & Renderable Exceptions -Instead of type-checking exceptions in the exception handler's `report` and `render` methods, you may define `report` and `render` methods directly on your custom exception. When these methods exist, they will be called automatically by the framework: +Instead of type-checking exceptions in the exception handler's `register` method, you may define `report` and `render` methods directly on your custom exceptions. When these methods exist, they will be automatically called by the framework: <?php @@ -138,7 +140,7 @@ Instead of type-checking exceptions in the exception handler's `report` and `ren use Exception; - class RenderException extends Exception + class InvalidOrderException extends Exception { /** * Report the exception. @@ -162,7 +164,7 @@ Instead of type-checking exceptions in the exception handler's `report` and `ren } } -If your exception contains custom reporting logic that only occurs when certain conditions are met, you may need to instruct Laravel to report the exception using the default exception handling configuration. To accomplish this, you may return `false` from the exception's `report` method: +If your exception contains custom reporting logic that is only necessary when certain conditions are met, you may need to instruct Laravel to sometimes report the exception using the default exception handling configuration. To accomplish this, you may return `false` from the exception's `report` method: /** * Report the exception. @@ -188,10 +190,10 @@ Some exceptions describe HTTP error codes from the server. For example, this may <a name="custom-http-error-pages"></a> ### Custom HTTP Error Pages -Laravel makes it easy to display custom error pages for various HTTP status codes. For example, if you wish to customize the error page for 404 HTTP status codes, create a `resources/views/errors/404.blade.php`. This file will be served on all 404 errors generated by your application. The views within this directory should be named to match the HTTP status code they correspond to. The `HttpException` instance raised by the `abort` function will be passed to the view as an `$exception` variable: +Laravel makes it easy to display custom error pages for various HTTP status codes. For example, if you wish to customize the error page for 404 HTTP status codes, create a `resources/views/errors/404.blade.php`. This file will be served on all 404 errors generated by your application. The views within this directory should be named to match the HTTP status code they correspond to. The `Symfony\Component\HttpKernel\Exception\HttpException` instance raised by the `abort` function will be passed to the view as an `$exception` variable: <h2>{{ $exception->getMessage() }}</h2> -You may publish Laravel's error page templates using the `vendor:publish` Artisan command. Once the templates have been published, you may customize them to your liking: +You may publish Laravel's default error page templates using the `vendor:publish` Artisan command. Once the templates have been published, you may customize them to your liking: php artisan vendor:publish --tag=laravel-errors From 0585604a9f9cc8fa7a33e0351d06a43a4dcbe51b Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 16:26:20 -0600 Subject: [PATCH 085/274] wip --- logging.md | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/logging.md b/logging.md index 4e3fe88f11..98779ae7fd 100644 --- a/logging.md +++ b/logging.md @@ -2,6 +2,8 @@ - [Introduction](#introduction) - [Configuration](#configuration) + - [Available Channel Drivers](#available-channel-drivers) + - [Channel Prerequisites](#channel-prerequisites) - [Building Log Stacks](#building-log-stacks) - [Writing Log Messages](#writing-log-messages) - [Writing To Specific Channels](#writing-to-specific-channels) @@ -36,7 +38,9 @@ By default, Monolog is instantiated with a "channel name" that matches the curre ], <a name="available-channel-drivers"></a> -#### Available Channel Drivers +### Available Channel Drivers + +The following log channel drivers are available in every Laravel application. An entry for most of these drivers is already present in your application's `config/logging.php` configuration file, so be sure to review this file to become familiar with its contents: Name | Description ------------- | ------------- @@ -49,9 +53,13 @@ Name | Description `errorlog` | A `ErrorLogHandler` based Monolog driver `monolog` | A Monolog factory driver that may use any supported Monolog handler `custom` | A driver that calls a specified factory to create a channel +`null` | A driver that discards all log messages > {tip} Check out the documentation on [advanced channel customization](#advanced-monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. +<a name="channel-prerequisites"></a> +### Channel Prerequisites + <a name="configuring-the-single-and-daily-channels"></a> #### Configuring The Single and Daily Channels @@ -66,12 +74,12 @@ Name | Description | Default <a name="configuring-the-papertrail-channel"></a> #### Configuring The Papertrail Channel -The `papertrail` channel requires the `url` and `port` configuration options. You can obtain these values from [Papertrail](https://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-php-apps/#send-events-from-php-app). +The `papertrail` channel requires the `host` and `port` configuration options. You can obtain these values from [Papertrail](https://help.papertrailapp.com/kb/configuration/configuring-centralized-logging-from-php-apps/#send-events-from-php-app). <a name="configuring-the-slack-channel"></a> #### Configuring The Slack Channel -The `slack` channel requires a `url` configuration option. This URL should match a URL for an [incoming webhook](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks) that you have configured for your Slack team. By default, Slack will only receive logs at the `critical` level and above; however, you can adjust this in your `logging` configuration file. +The `slack` channel requires a `url` configuration option. This URL should match a URL for an [incoming webhook](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks) that you have configured for your Slack team. By default, Slack will only receive logs at the `critical` level and above; however, you can adjust this in your `config/logging.php` configuration file by modifying the `level` configuration option within your Slack log channel's configuration array. <a name="building-log-stacks"></a> ### Building Log Stacks @@ -118,6 +126,8 @@ Given our configuration, the `syslog` channel will write the message to the syst You may write information to the logs using the `Log` [facade](/docs/{{version}}/facades). As previously mentioned, the logger provides the eight logging levels defined in the [RFC 5424 specification](https://tools.ietf.org/html/rfc5424): **emergency**, **alert**, **critical**, **error**, **warning**, **notice**, **info** and **debug**: + use Illuminate\Support\Facades\Log; + Log::emergency($message); Log::alert($message); Log::critical($message); @@ -127,7 +137,7 @@ You may write information to the logs using the `Log` [facade](/docs/{{version}} Log::info($message); Log::debug($message); -So, you may call any of these methods to log a message for the corresponding level. By default, the message will be written to the default log channel as configured by your `config/logging.php` configuration file: +You may call any of these methods to log a message for the corresponding level. By default, the message will be written to the default log channel as configured by your `logging` configuration file: <?php @@ -143,13 +153,15 @@ So, you may call any of these methods to log a message for the corresponding lev * Show the profile for the given user. * * @param int $id - * @return Response + * @return \Illuminate\Http\Response */ - public function showProfile($id) + public function show($id) { - Log::info('Showing user profile for user: '.$id); + Log::info('Showing the user profile for user: '.$id); - return view('user.profile', ['user' => User::findOrFail($id)]); + return view('user.profile', [ + 'user' => User::findOrFail($id) + ]); } } @@ -165,6 +177,8 @@ An array of contextual data may also be passed to the log methods. This contextu Sometimes you may wish to log a message to a channel other than your application's default channel. You may use the `channel` method on the `Log` facade to retrieve and log to any channel defined in your configuration file: + use Illuminate\Support\Facades\Log; + Log::channel('slack')->info('Something happened!'); If you would like to create an on-demand logging stack consisting of multiple channels, you may use the `stack` method: @@ -180,7 +194,7 @@ If you would like to create an on-demand logging stack consisting of multiple ch Sometimes you may need complete control over how Monolog is configured for an existing channel. For example, you may want to configure a custom Monolog `FormatterInterface` implementation for a given channel's handlers. -To get started, define a `tap` array on the channel's configuration. The `tap` array should contain a list of classes that should have an opportunity to customize (or "tap" into) the Monolog instance after it is created: +To get started, define a `tap` array on the channel's configuration. The `tap` array should contain a list of classes that should have an opportunity to customize (or "tap" into) the Monolog instance after it is created. There is no conventional location where these classes should be placed, so you are free to create a directory within your application to contain these classes: 'single' => [ 'driver' => 'single', @@ -258,16 +272,16 @@ If you are using a Monolog handler that is capable of providing its own formatte <a name="creating-channels-via-factories"></a> ### Creating Channels Via Factories -If you would like to define an entirely custom channel in which you have full control over Monolog's instantiation and configuration, you may specify a `custom` driver type in your `config/logging.php` configuration file. Your configuration should include a `via` option to point to the factory class which will be invoked to create the Monolog instance: +If you would like to define an entirely custom channel in which you have full control over Monolog's instantiation and configuration, you may specify a `custom` driver type in your `config/logging.php` configuration file. Your configuration should include a `via` option that contains the name of the factory class which will be invoked to create the Monolog instance: 'channels' => [ - 'custom' => [ + 'example-custom-channel' => [ 'driver' => 'custom', 'via' => App\Logging\CreateCustomLogger::class, ], ], -Once you have configured the `custom` channel, you're ready to define the class that will create your Monolog instance. This class only needs a single method: `__invoke`, which should return the Monolog instance: +Once you have configured the `custom` driver channel, you're ready to define the class that will create your Monolog instance. This class only needs a single `__invoke` method which should return the Monolog logger instance. The method will receive the channels configuration array as its only argument: <?php From 56c17c482e82e133ec945c618ef02227d40ecf21 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 16:37:59 -0600 Subject: [PATCH 086/274] wip --- logging.md | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/logging.md b/logging.md index 98779ae7fd..50678c96a8 100644 --- a/logging.md +++ b/logging.md @@ -4,25 +4,27 @@ - [Configuration](#configuration) - [Available Channel Drivers](#available-channel-drivers) - [Channel Prerequisites](#channel-prerequisites) - - [Building Log Stacks](#building-log-stacks) +- [Building Log Stacks](#building-log-stacks) - [Writing Log Messages](#writing-log-messages) - [Writing To Specific Channels](#writing-to-specific-channels) - [Advanced Monolog Channel Customization](#advanced-monolog-channel-customization) - [Customizing Monolog For Channels](#customizing-monolog-for-channels) - [Creating Monolog Handler Channels](#creating-monolog-handler-channels) - - [Creating Channels Via Factories](#creating-channels-via-factories) + - [Creating Custom Channels Via Factories](#creating-custom-channels-via-factories) <a name="introduction"></a> ## Introduction To help you learn more about what's happening within your application, Laravel provides robust logging services that allow you to log messages to files, the system error log, and even to Slack to notify your entire team. +Laravel logging is based on "channels". Each channel represents a specific way of writing log information. For example, the `single` channel writes log files to a single log file, while the `slack` channel sends log messages to Slack. Log messages may be written to multiple channels based on their severity. + Under the hood, Laravel utilizes the [Monolog](https://github.com/Seldaek/monolog) library, which provides support for a variety of powerful log handlers. Laravel makes it a cinch to configure these handlers, allowing you to mix and match them to customize your application's log handling. <a name="configuration"></a> ## Configuration -All of the configuration for your application's logging system is housed in the `config/logging.php` configuration file. This file allows you to configure your application's log channels, so be sure to review each of the available channels and their options. We'll review a few common options below. +All of the configuration options for your application's logging behavior is housed in the `config/logging.php` configuration file. This file allows you to configure your application's log channels, so be sure to review each of the available channels and their options. We'll review a few common options below. By default, Laravel will use the `stack` channel when logging messages. The `stack` channel is used to aggregate multiple log channels into a single channel. For more information on building stacks, check out the [documentation below](#building-log-stacks). @@ -40,20 +42,20 @@ By default, Monolog is instantiated with a "channel name" that matches the curre <a name="available-channel-drivers"></a> ### Available Channel Drivers -The following log channel drivers are available in every Laravel application. An entry for most of these drivers is already present in your application's `config/logging.php` configuration file, so be sure to review this file to become familiar with its contents: +Each log channel is powered by a "driver". The driver determines how and where the log message is actually recorded. The following log channel drivers are available in every Laravel application. An entry for most of these drivers is already present in your application's `config/logging.php` configuration file, so be sure to review this file to become familiar with its contents: Name | Description ------------- | ------------- -`stack` | A wrapper to facilitate creating "multi-channel" channels -`single` | A single file or path based logger channel (`StreamHandler`) +`custom` | A driver that calls a specified factory to create a channel `daily` | A `RotatingFileHandler` based Monolog driver which rotates daily -`slack` | A `SlackWebhookHandler` based Monolog driver -`papertrail` | A `SyslogUdpHandler` based Monolog driver -`syslog` | A `SyslogHandler` based Monolog driver `errorlog` | A `ErrorLogHandler` based Monolog driver `monolog` | A Monolog factory driver that may use any supported Monolog handler -`custom` | A driver that calls a specified factory to create a channel `null` | A driver that discards all log messages +`papertrail` | A `SyslogUdpHandler` based Monolog driver +`single` | A single file or path based logger channel (`StreamHandler`) +`slack` | A `SlackWebhookHandler` based Monolog driver +`stack` | A wrapper to facilitate creating "multi-channel" channels +`syslog` | A `SyslogHandler` based Monolog driver > {tip} Check out the documentation on [advanced channel customization](#advanced-monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. @@ -68,8 +70,8 @@ The `single` and `daily` channels have three optional configuration options: `bu Name | Description | Default ------------- | ------------- | ------------- `bubble` | Indicates if messages should bubble up to other channels after being handled | `true` -`permission` | The log file's permissions | `0644` `locking` | Attempt to lock the log file before writing to it | `false` +`permission` | The log file's permissions | `0644` <a name="configuring-the-papertrail-channel"></a> #### Configuring The Papertrail Channel @@ -79,12 +81,14 @@ The `papertrail` channel requires the `host` and `port` configuration options. Y <a name="configuring-the-slack-channel"></a> #### Configuring The Slack Channel -The `slack` channel requires a `url` configuration option. This URL should match a URL for an [incoming webhook](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks) that you have configured for your Slack team. By default, Slack will only receive logs at the `critical` level and above; however, you can adjust this in your `config/logging.php` configuration file by modifying the `level` configuration option within your Slack log channel's configuration array. +The `slack` channel requires a `url` configuration option. This URL should match a URL for an [incoming webhook](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks) that you have configured for your Slack team. + +By default, Slack will only receive logs at the `critical` level and above; however, you can adjust this in your `config/logging.php` configuration file by modifying the `level` configuration option within your Slack log channel's configuration array. <a name="building-log-stacks"></a> -### Building Log Stacks +## Building Log Stacks -As previously mentioned, the `stack` driver allows you to combine multiple channels into a single log channel. To illustrate how to use log stacks, let's take a look at an example configuration that you might see in a production application: +As mentioned previously, the `stack` driver allows you to combine multiple channels into a single log channel for convenience. To illustrate how to use log stacks, let's take a look at an example configuration that you might see in a production application: 'channels' => [ 'stack' => [ @@ -106,7 +110,7 @@ As previously mentioned, the `stack` driver allows you to combine multiple chann ], ], -Let's dissect this configuration. First, notice our `stack` channel aggregates two other channels via its `channels` option: `syslog` and `slack`. So, when logging messages, both of these channels will have the opportunity to log the message. +Let's dissect this configuration. First, notice our `stack` channel aggregates two other channels via its `channels` option: `syslog` and `slack`. So, when logging messages, both of these channels will have the opportunity to log the message. However, as we will see below, whether these channels actually log the message may be determined by the message's severity / "level". <a name="log-levels"></a> #### Log Levels @@ -269,8 +273,8 @@ If you are using a Monolog handler that is capable of providing its own formatte 'formatter' => 'default', ], -<a name="creating-channels-via-factories"></a> -### Creating Channels Via Factories +<a name="creating-custom-channels-via-factories"></a> +### Creating Custom Channels Via Factories If you would like to define an entirely custom channel in which you have full control over Monolog's instantiation and configuration, you may specify a `custom` driver type in your `config/logging.php` configuration file. Your configuration should include a `via` option that contains the name of the factory class which will be invoked to create the Monolog instance: From cb2267a4ca74e0c46ec4d5df89547bba0fba04a6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 16:38:31 -0600 Subject: [PATCH 087/274] wip --- logging.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/logging.md b/logging.md index 50678c96a8..69dc9dd3c1 100644 --- a/logging.md +++ b/logging.md @@ -7,7 +7,7 @@ - [Building Log Stacks](#building-log-stacks) - [Writing Log Messages](#writing-log-messages) - [Writing To Specific Channels](#writing-to-specific-channels) -- [Advanced Monolog Channel Customization](#advanced-monolog-channel-customization) +- [Monolog Channel Customization](#monolog-channel-customization) - [Customizing Monolog For Channels](#customizing-monolog-for-channels) - [Creating Monolog Handler Channels](#creating-monolog-handler-channels) - [Creating Custom Channels Via Factories](#creating-custom-channels-via-factories) @@ -190,8 +190,8 @@ If you would like to create an on-demand logging stack consisting of multiple ch Log::stack(['single', 'slack'])->info('Something happened!'); -<a name="advanced-monolog-channel-customization"></a> -## Advanced Monolog Channel Customization +<a name="monolog-channel-customization"></a> +## Monolog Channel Customization <a name="customizing-monolog-for-channels"></a> ### Customizing Monolog For Channels From cdc64f8c28f82e6163124d8cce76e8eacd7b3133 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 16:40:04 -0600 Subject: [PATCH 088/274] fix link --- logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logging.md b/logging.md index 69dc9dd3c1..4d0208649f 100644 --- a/logging.md +++ b/logging.md @@ -57,7 +57,7 @@ Name | Description `stack` | A wrapper to facilitate creating "multi-channel" channels `syslog` | A `SyslogHandler` based Monolog driver -> {tip} Check out the documentation on [advanced channel customization](#advanced-monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. +> {tip} Check out the documentation on [advanced channel customization](#monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. <a name="channel-prerequisites"></a> ### Channel Prerequisites From 09e6cccf7d18b5bb58541f8b87e0fb12512dd698 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Mon, 16 Nov 2020 16:46:51 -0600 Subject: [PATCH 089/274] wip --- logging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logging.md b/logging.md index 4d0208649f..c6db47dffb 100644 --- a/logging.md +++ b/logging.md @@ -196,7 +196,7 @@ If you would like to create an on-demand logging stack consisting of multiple ch <a name="customizing-monolog-for-channels"></a> ### Customizing Monolog For Channels -Sometimes you may need complete control over how Monolog is configured for an existing channel. For example, you may want to configure a custom Monolog `FormatterInterface` implementation for a given channel's handlers. +Sometimes you may need complete control over how Monolog is configured for an existing channel. For example, you may want to configure a custom Monolog `FormatterInterface` implementation for Laravel's built-in `single` channel. To get started, define a `tap` array on the channel's configuration. The `tap` array should contain a list of classes that should have an opportunity to customize (or "tap" into) the Monolog instance after it is created. There is no conventional location where these classes should be placed, so you are free to create a directory within your application to contain these classes: @@ -238,7 +238,7 @@ Once you have configured the `tap` option on your channel, you're ready to defin <a name="creating-monolog-handler-channels"></a> ### Creating Monolog Handler Channels -Monolog has a variety of [available handlers](https://github.com/Seldaek/monolog/tree/master/src/Monolog/Handler). In some cases, the type of logger you wish to create is merely a Monolog driver with an instance of a specific handler. These channels can be created using the `monolog` driver. +Monolog has a variety of [available handlers](https://github.com/Seldaek/monolog/tree/master/src/Monolog/Handler) and Laravel does not include a built-in channel for each one. In some cases, you may wish to create a custom channel that is merely an instance of a specific Monolog handler that does not have a corresponding Laravel log driver. These channels can be easily created using the `monolog` driver. When using the `monolog` driver, the `handler` configuration option is used to specify which handler will be instantiated. Optionally, any constructor parameters the handler needs may be specified using the `with` configuration option: From 6e3ee828486ea461d245b12f84930a038a49a128 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 09:40:35 -0600 Subject: [PATCH 090/274] wip --- controllers.md | 8 ++++---- errors.md | 15 ++++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/controllers.md b/controllers.md index 653f29ea92..c41a10cc7d 100644 --- a/controllers.md +++ b/controllers.md @@ -26,7 +26,7 @@ Instead of defining all of your request handling logic as closures in your route <a name="basic-controllers"></a> ### Basic Controllers -Below is an example of a basic controller class. Note that the controller extends the base controller class included with Laravel: `App\Http\Controllers\Controller`. The base class provides a few convenience methods such as the `middleware` method, which may be used to attach middleware to controller actions: +Let's take a look at an example of a basic controller. Note that the controller extends the base controller class included with Laravel: `App\Http\Controllers\Controller`: <?php @@ -51,13 +51,13 @@ Below is an example of a basic controller class. Note that the controller extend } } -You can define a route to this controller action like so: +You can define a route to this controller method like so: use App\Http\Controllers\UserController; Route::get('/user/{id}', [UserController::class, 'show']); -When an incoming request matches the specified route URI, the `show` method on the `UserController` class will be invoked and the route parameters will be passed to the method. +When an incoming request matches the specified route URI, the `show` method on the `App\Http\Controllers\UserController` class will be invoked and the route parameters will be passed to the method. > {tip} Controllers are not **required** to extend a base class. However, you will not have access to convenient features such as the `middleware` and `authorize` methods. @@ -106,7 +106,7 @@ You may generate an invokable controller by using the `--invokable` option of th Route::get('profile', [UserController::class, 'show'])->middleware('auth'); -Or, you may find it convenient to specify middleware within your controller's constructor. Using the `middleware` method from your controller's constructor, you can assign middleware to the controller's actions: +Or, you may find it convenient to specify middleware within your controller's constructor. Using the `middleware` method within your controller's constructor, you can assign middleware to the controller's actions: class UserController extends Controller { diff --git a/errors.md b/errors.md index 80a81db273..f0fe6ea8d3 100644 --- a/errors.md +++ b/errors.md @@ -4,6 +4,7 @@ - [Configuration](#configuration) - [The Exception Handler](#the-exception-handler) - [Reporting Exceptions](#reporting-exceptions) + - [Ignoring Exceptions By Type](#ignoring-exceptions-by-type) - [Rendering Exceptions](#rendering-exceptions) - [Reportable & Renderable Exceptions](#renderable-exceptions) - [HTTP Exceptions](#http-exceptions) @@ -91,9 +92,11 @@ Sometimes you may need to report an exception but continue handling the current } <a name="ignoring-exceptions-by-type"></a> -#### Ignoring Exceptions By Type +### Ignoring Exceptions By Type -The `$dontReport` property of the exception handler contains an array of exception types that will not be reported or logged. For example, exceptions resulting from 404 HTTP errors, as well as several other types of errors, are not written to your log files. You may add other exception types to this array as needed: +When building your application, there will be some types of exceptions you simply want to ignore and never report. Your application's exception handler contains a `$dontReport` property which is initialized to an empty array. Any classes that you add to this property will never be reported; however, they may still have custom rendering logic: + + use App\Exceptions\InvalidOrderException; /** * A list of the exception types that should not be reported. @@ -101,13 +104,11 @@ The `$dontReport` property of the exception handler contains an array of excepti * @var array */ protected $dontReport = [ - \Illuminate\Auth\AuthenticationException::class, - \Illuminate\Auth\Access\AuthorizationException::class, - \Symfony\Component\HttpKernel\Exception\HttpException::class, - \Illuminate\Database\Eloquent\ModelNotFoundException::class, - \Illuminate\Validation\ValidationException::class, + InvalidOrderException::class, ]; +> {tip} Behind the scenes, Laravel already ignores some types of errors for you, such as exceptions resulting from 404 HTTP "not found" errors or 419 HTTP responses generated by invalid CSRF tokens. + <a name="rendering-exceptions"></a> ### Rendering Exceptions From 79b62dbe0cd5b2343a8cf4031eb7d808aa5f6c37 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 10:51:00 -0600 Subject: [PATCH 091/274] wip --- artisan.md | 240 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 140 insertions(+), 100 deletions(-) diff --git a/artisan.md b/artisan.md index a92e0ea573..588afc31cb 100644 --- a/artisan.md +++ b/artisan.md @@ -24,7 +24,7 @@ <a name="introduction"></a> ## Introduction -Artisan is the command-line interface included with Laravel. It provides a number of helpful commands that can assist you while you build your application. To view a list of all available Artisan commands, you may use the `list` command: +Artisan is the command line interface included with Laravel. Artisan exists at the root of your application as the `artisan` script and provides a number of helpful commands that can assist you while you build your application. To view a list of all available Artisan commands, you may use the `list` command: php artisan list @@ -32,6 +32,13 @@ Every command also includes a "help" screen which displays and describes the com php artisan help migrate +<a name="laravel-sail"></a> +#### Laravel Sail + +If you are using [Laravel Sail](/docs/{{version}}/installation#laravel-sail) as your local development environment, remember to use the `sail` command line to invoke Artisan commands. Sail will execute your Artisan commands within your application's Docker containers: + + ./sail artisan list + <a name="tinker"></a> ### Tinker (REPL) @@ -40,7 +47,7 @@ Laravel Tinker is a powerful REPL for the Laravel framework, powered by the [Psy <a name="installation"></a> #### Installation -All Laravel applications include Tinker by default. However, you may install it manually if needed using Composer: +All Laravel applications include Tinker by default. However, you may install Tinker using Composer if you have previously removed it from your application: composer require laravel/tinker @@ -49,7 +56,7 @@ All Laravel applications include Tinker by default. However, you may install it <a name="usage"></a> #### Usage -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: +Tinker allows you to interact with your entire Laravel application on the command line, including your Eloquent models, jobs, events, and more. To enter the Tinker environment, run the `tinker` Artisan command: php artisan tinker @@ -59,10 +66,10 @@ You can publish Tinker's configuration file using the `vendor:publish` command: > {note} The `dispatch` helper function and `dispatch` method on the `Dispatchable` class depends on garbage collection to place the job on the queue. Therefore, when using tinker, you should use `Bus::dispatch` or `Queue::push` to dispatch jobs. -<a name="command-whitelist"></a> -#### Command Whitelist +<a name="command-allow-list"></a> +#### Command Allow List -Tinker utilizes a white-list to determine which Artisan commands are allowed to be run within its shell. By default, you may run the `clear-compiled`, `down`, `env`, `inspire`, `migrate`, `optimize`, and `up` commands. If you would like to white-list more commands you may add them to the `commands` array in your `tinker.php` configuration file: +Tinker utilizes an "allow" list to determine which Artisan commands are allowed to be run within its shell. By default, you may run the `clear-compiled`, `down`, `env`, `inspire`, `migrate`, `optimize`, and `up` commands. If you would like to allow more commands you may add them to the `commands` array in your `tinker.php` configuration file: 'commands' => [ // App\Console\Commands\ExampleCommand::class, @@ -71,7 +78,7 @@ Tinker utilizes a white-list to determine which Artisan commands are allowed to <a name="classes-that-should-not-be-aliased"></a> #### Classes That Should Not Be Aliased -Typically, Tinker automatically aliases classes as you require them in Tinker. However, you may wish to never alias some classes. You may accomplish this by listing the classes in the `dont_alias` array of your `tinker.php` configuration file: +Typically, Tinker automatically aliases classes as you interact with them in Tinker. However, you may wish to never alias some classes. You may accomplish this by listing the classes in the `dont_alias` array of your `tinker.php` configuration file: 'dont_alias' => [ App\Models\User::class, @@ -80,23 +87,21 @@ Typically, Tinker automatically aliases classes as you require them in Tinker. H <a name="writing-commands"></a> ## Writing Commands -In addition to the commands provided with Artisan, you may also build your own custom commands. Commands are typically stored in the `app/Console/Commands` directory; however, you are free to choose your own storage location as long as your commands can be loaded by Composer. +In addition to the commands provided with Artisan, you may build your own custom commands. Commands are typically stored in the `app/Console/Commands` directory; however, you are free to choose your own storage location as long as your commands can be loaded by Composer. <a name="generating-commands"></a> ### 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. 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: +To create a new command, you may 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 - it will be created the first time you run the `make:command` Artisan command: php artisan make:command SendEmails <a name="command-structure"></a> ### Command Structure -After generating your command, you should fill in the `signature` and `description` properties of the class, which will be used when displaying your command on the `list` screen. The `handle` method will be called when your command is executed. You may place your command logic in this method. - -> {tip} For greater code reuse, it is good practice to keep your console commands light and let them defer to application services to accomplish their tasks. In the example below, note that we inject a service class to do the "heavy lifting" of sending the e-mails. +After generating your command, you should define appropriate values for the `signature` and `description` properties of the class. These properties will be used when displaying your command on the `list` screen. The `signature` property also allows you to define [your command's input expectations](#defining-input-expectations). The `handle` method will be called when your command is executed. You may place your command logic in this method. -Let's take a look at an example command. Note that we are able to inject any dependencies we need into the command's `handle` method. The Laravel [service container](/docs/{{version}}/container) will automatically inject all dependencies that are type-hinted in this method's signature: +Let's take a look at an example command. Note that we are able to request any dependencies we need via the command's `handle` method. The Laravel [service container](/docs/{{version}}/container) will automatically inject all dependencies that are type-hinted in this method's signature: <?php @@ -113,14 +118,14 @@ Let's take a look at an example command. Note that we are able to inject any dep * * @var string */ - protected $signature = 'email:send {user}'; + protected $signature = 'mail:send {user}'; /** * The console command description. * * @var string */ - protected $description = 'Send drip e-mails to a user'; + protected $description = 'Send a marketing email to a user'; /** * Create a new command instance. @@ -144,6 +149,8 @@ Let's take a look at an example command. Note that we are able to inject any dep } } +> {tip} For greater code reuse, it is good practice to keep your console commands light and let them defer to application services to accomplish their tasks. In the example above, note that we inject a service class to do the "heavy lifting" of sending the e-mails. + <a name="closure-commands"></a> ### Closure Commands @@ -159,10 +166,10 @@ Closure based commands provide an alternative to defining console commands as cl 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: +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 console commands 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}!"); + Artisan::command('mail:send {user}', function ($user) { + $this->info("Sending email to: {$user}!"); }); 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. @@ -175,18 +182,18 @@ In addition to receiving your command's arguments and options, command closures use App\Models\User; use App\Support\DripEmailer; - Artisan::command('email:send {user}', function (DripEmailer $drip, $user) { + Artisan::command('mail:send {user}', function (DripEmailer $drip, $user) { $drip->send(User::find($user)); }); <a name="closure-command-descriptions"></a> #### 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: +When defining a closure based command, you may use the `purpose` 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'); + Artisan::command('mail:send {user}', function ($user) { + // ... + })->purpose('Send a marketing email to a user'); <a name="defining-input-expectations"></a> ## Defining Input Expectations @@ -196,94 +203,97 @@ When writing console commands, it is common to gather input from the user throug <a name="arguments"></a> ### Arguments -All user supplied arguments and options are wrapped in curly braces. In the following example, the command defines one **required** argument: `user`: +All user supplied arguments and options are wrapped in curly braces. In the following example, the command defines one required argument: `user`: /** * The name and signature of the console command. * * @var string */ - protected $signature = 'email:send {user}'; + protected $signature = 'mail:send {user}'; -You may also make arguments optional and define default values for arguments: +You may also make arguments optional or define default values for arguments: // Optional argument... - email:send {user?} + mail:send {user?} // Optional argument with default value... - email:send {user=foo} + mail:send {user=foo} <a name="options"></a> ### Options -Options, like arguments, are another form of user input. Options are prefixed by two hyphens (`--`) when they are specified on the command line. There are two types of options: those that receive a value and those that don't. Options that don't receive a value serve as a boolean "switch". Let's take a look at an example of this type of option: +Options, like arguments, are another form of user input. Options are prefixed by two hyphens (`--`) when they are provided via the command line. There are two types of options: those that receive a value and those that don't. Options that don't receive a value serve as a boolean "switch". Let's take a look at an example of this type of option: /** * The name and signature of the console command. * * @var string */ - protected $signature = 'email:send {user} {--queue}'; + protected $signature = 'mail:send {user} {--queue}'; In this example, the `--queue` switch may be specified when calling the Artisan command. If the `--queue` switch is passed, the value of the option will be `true`. Otherwise, the value will be `false`: - php artisan email:send 1 --queue + php artisan mail:send 1 --queue <a name="options-with-values"></a> #### Options With Values -Next, let's take a look at an option that expects a value. If the user must specify a value for an option, suffix the option name with a `=` sign: +Next, let's take a look at an option that expects a value. If the user must specify a value for an option, you should suffix the option name with a `=` sign: /** * The name and signature of the console command. * * @var string */ - protected $signature = 'email:send {user} {--queue=}'; + protected $signature = 'mail:send {user} {--queue=}'; -In this example, the user may pass a value for the option like so: +In this example, the user may pass a value for the option like so. If the option is not specified when invoking the command, its value will be `null`: - php artisan email:send 1 --queue=default + php artisan mail:send 1 --queue=default You may assign default values to options by specifying the default value after the option name. If no option value is passed by the user, the default value will be used: - email:send {user} {--queue=default} + mail:send {user} {--queue=default} <a name="option-shortcuts"></a> #### Option Shortcuts -To assign a shortcut when defining an option, you may specify it before the option name and use a | delimiter to separate the shortcut from the full option name: +To assign a shortcut when defining an option, you may specify it before the option name and use the `|` character as a delimiter to separate the shortcut from the full option name: - email:send {user} {--Q|queue} + mail:send {user} {--Q|queue} <a name="input-arrays"></a> ### Input Arrays -If you would like to define arguments or options to expect array inputs, you may use the `*` character. First, let's take a look at an example that specifies an array argument: +If you would like to define arguments or options to expect multiple input values, you may use the `*` character. First, let's take a look at an example that specifies such an argument: + + mail:send {user*} - email:send {user*} +When calling this method, the `user` arguments may be passed in order to the command line. For example, the following command will set the value of `user` to an array with `foo` and `bar` as its values: -When calling this method, the `user` arguments may be passed in order to the command line. For example, the following command will set the value of `user` to `['foo', 'bar']`: + php artisan mail:send foo bar - php artisan email:send foo bar +<a name="option-arrays"></a> +#### Option Arrays -When defining an option that expects an array input, each option value passed to the command should be prefixed with the option name: +When defining an option that expects multiple input values, each option value passed to the command should be prefixed with the option name: - email:send {user} {--id=*} + mail:send {user} {--id=*} - php artisan email:send --id=1 --id=2 + php artisan mail:send --id=1 --id=2 <a name="input-descriptions"></a> ### Input Descriptions -You may assign descriptions to input arguments and options by separating the parameter from the description using a colon. If you need a little extra room to define your command, feel free to spread the definition across multiple lines: +You may assign descriptions to input arguments and options by separating the argument name from the description using a colon. If you need a little extra room to define your command, feel free to spread the definition across multiple lines: /** * The name and signature of the console command. * * @var string */ - protected $signature = 'email:send + protected $signature = 'mail:send {user : The ID of the user} {--queue= : Whether the job should be queued}'; @@ -293,12 +303,12 @@ You may assign descriptions to input arguments and options by separating the par <a name="retrieving-input"></a> ### Retrieving Input -While your command is executing, you will obviously need to access the values for the arguments and options accepted by your command. To do so, you may use the `argument` and `option` methods: +While your command is executing, you will likely need to access the values for the arguments and options accepted by your command. To do so, you may use the `argument` and `option` methods. If an argument or option does not exist, `null` will be returned: /** * Execute the console command. * - * @return mixed + * @return int */ public function handle() { @@ -316,11 +326,9 @@ Options may be retrieved just as easily as arguments using the `option` method. // Retrieve a specific option... $queueName = $this->option('queue'); - // Retrieve all options... + // Retrieve all options as an array... $options = $this->options(); -If the argument or option does not exist, `null` will be returned. - <a name="prompting-for-input"></a> ### Prompting For Input @@ -336,38 +344,48 @@ In addition to displaying output, you may also ask the user to provide input dur $name = $this->ask('What is your name?'); } -The `secret` method is similar to `ask`, but the user's input will not be visible to them as they type in the console. This method is useful when asking for sensitive information such as a password: +The `secret` method is similar to `ask`, but the user's input will not be visible to them as they type in the console. This method is useful when asking for sensitive information such as passwords: $password = $this->secret('What is the password?'); <a name="asking-for-confirmation"></a> #### 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` or `yes` in response to the prompt, the method will return `true`. +If you need to ask the user for a simple "yes or no" 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?')) { // } +If necessary, you may specify that the confirmation prompt should return `true` by default by passing `true` as the second argument to the `confirm` method: + + if ($this->confirm('Do you wish to continue?', true)) { + // + } + <a name="auto-completion"></a> #### Auto-Completion -The `anticipate` method can be used to provide auto-completion for possible choices. The user can still choose any answer, regardless of the auto-completion hints: +The `anticipate` method can be used to provide auto-completion for possible choices. The user can still provide any answer, regardless of the auto-completion hints: $name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']); Alternatively, you may pass a closure as the second argument to the `anticipate` method. The closure will be called each time the user types an input character. The closure should accept a string parameter containing the user's input so far, and return an array of options for auto-completion: - $name = $this->anticipate('What is your name?', function ($input) { + $name = $this->anticipate('What is your address?', function ($input) { // Return auto-completion options... }); <a name="multiple-choice-questions"></a> #### Multiple Choice Questions -If you need to give the user a predefined set of choices, you may use the `choice` method. You may set the array index of the default value to be returned if no option is chosen: +If you need to give the user a predefined set of choices when asking a question, you may use the `choice` method. You may set the array index of the default value to be returned if no option is chosen by passing the index as the third argument to the method: - $name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex); + $name = $this->choice( + 'What is your name?', + ['Taylor', 'Dayle'], + $defaultIndex + ); In addition, the `choice` method accepts optional fourth and fifth arguments for determining the maximum number of attempts to select a valid response and whether multiple selections are permitted: @@ -382,7 +400,7 @@ In addition, the `choice` method accepts optional fourth and fifth arguments for <a name="writing-output"></a> ### Writing Output -To send output to the console, use the `line`, `info`, `comment`, `question` and `error` methods. Each of these methods will use appropriate ANSI colors for their purpose. For example, let's display some general information to the user. Typically, the `info` method will display in the console as green text: +To send output to the console, you may use the `line`, `info`, `comment`, `question` and `error` methods. Each of these methods will use appropriate ANSI colors for their purpose. For example, let's display some general information to the user. Typically, the `info` method will display in the console as green colored text: /** * Execute the console command. @@ -391,39 +409,52 @@ To send output to the console, use the `line`, `info`, `comment`, `question` and */ public function handle() { - $this->info('Display this on the screen'); + // ... + + $this->info('The command was successful!'); } To display an error message, use the `error` method. Error message text is typically displayed in red: $this->error('Something went wrong!'); -If you would like to display plain, uncolored console output, use the `line` method: +You may use the `line` method to display plain, uncolored text: $this->line('Display this on the screen'); You may use the `newLine` method to display a blank line: + // Write a single blank line... $this->newLine(); // Write three blank lines... $this->newLine(3); -<a name="table-layouts"></a> -#### Table Layouts - -The `table` method makes it easy to correctly format multiple rows / columns of data. Just pass in the headers and rows to the method. The width and height will be dynamically calculated based on the given data: +<a name="tables"></a> +#### Tables - $headers = ['Name', 'Email']; +The `table` method makes it easy to correctly format multiple rows / columns of data. All you need to do is provide the column names and the data for the table and Laravel will +automatically calculate the appropriate width and height of the table for you: - $users = App\Models\User::all(['name', 'email'])->toArray(); + use App\Models\User; - $this->table($headers, $users); + $this->table( + ['Name', 'Email'], + User::all(['name', 'email'])->toArray() + ); <a name="progress-bars"></a> #### Progress Bars -For long running tasks, it could be helpful to show a progress indicator. Using the output object, we can start, advance and stop the Progress Bar. First, define the total number of steps the process will iterate through. Then, advance the Progress Bar after processing each item: +For long running tasks, it can be helpful to show a progress bar that informs users how complete the task is. Using the `withProgressBar` method, Laravel will display a progress bar and advance its progress for each iteration over a given iterable value: + + use App\Models\User; + + $users = $this->withProgressBar(User::all(), function ($user) { + $this->performTask($user); + }); + +Sometimes, you may need more manual control over how a progress bar is advanced. First, define the total number of steps the process will iterate through. Then, advance the progress bar after processing each item: $users = App\Models\User::all(); @@ -439,12 +470,12 @@ 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](https://symfony.com/doc/current/components/console/helpers/progressbar.html). +> {tip} For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/current/components/console/helpers/progressbar.html). <a name="registering-commands"></a> ## Registering Commands -Because of the `load` method call in your console kernel's `commands` method, all commands within the `app/Console/Commands` directory will automatically be registered with Artisan. In fact, you are free to make additional calls to the `load` method to scan other directories for Artisan commands: +All of your console commands are registered within your application's `App\Console\Kernel` class, which is your application's "console kernel". Within the `commands` method of this class, you will see a call to the kernel's `load` method. The `load` method will scan the `app/Console/Commands` directory and automatically register each command it contains with Artisan. You are even free to make additional calls to the `load` method to scan other directories for Artisan commands: /** * Register the commands for the application. @@ -454,12 +485,12 @@ Because of the `load` method call in your console kernel's `commands` method, al protected function commands() { $this->load(__DIR__.'/Commands'); - $this->load(__DIR__.'/MoreCommands'); + $this->load(__DIR__.'/../Domain/Orders/Commands'); // ... } -You may also manually register commands by adding its class name to the `$commands` property of your `app/Console/Kernel.php` file. When Artisan boots, all the commands listed in this property will be resolved by the [service container](/docs/{{version}}/container) and registered with Artisan: +If necessary, you may manually register commands by adding the command's class name to the `$commands` property of your `App\Console\Kernel` class. When Artisan boots, all the commands listed in this property will be resolved by the [service container](/docs/{{version}}/container) and registered with Artisan: protected $commands = [ Commands\SendEmails::class @@ -468,56 +499,65 @@ You may also manually register commands by adding its class name to the `$comman <a name="programmatically-executing-commands"></a> ## 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 either the command's name or class as the first argument, and an array of command parameters as the second argument. The exit code will be returned: +Sometimes you may wish to execute an Artisan command outside of the CLI. For example, you may wish to execute 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 either the command's signature name or class name as its first argument, and an array of command parameters as the second argument. The exit code will be returned: - Route::get('/foo', function () { - $exitCode = Artisan::call('email:send', [ - 'user' => 1, '--queue' => 'default' - ]); - - // - }); - -Alternatively, you may pass the entire Artisan command to the `call` method as a string: + use Illuminate\Support\Facades\Artisan; - Artisan::call('email:send 1 --queue=default'); - -Using the `queue` method on the `Artisan` facade, you may even queue Artisan commands so they are processed in the background by your [queue workers](/docs/{{version}}/queues). Before using this method, make sure you have configured your queue and are running a queue listener: - - Route::get('/foo', function () { - Artisan::queue('email:send', [ - 'user' => 1, '--queue' => 'default' + Route::post('/user/{user}/mail', function ($user) { + $exitCode = Artisan::call('mail:send', [ + 'user' => $user, '--queue' => 'default' ]); // }); -You may also specify the connection or queue the Artisan command should be dispatched to: +Alternatively, you may pass the entire Artisan command to the `call` method as a string: - Artisan::queue('email:send', [ - 'user' => 1, '--queue' => 'default' - ])->onConnection('redis')->onQueue('commands'); + Artisan::call('mail:send 1 --queue=default'); <a name="passing-array-values"></a> #### Passing Array Values If your command defines an option that accepts an array, you may pass an array of values to that option: - Route::get('/foo', function () { - $exitCode = Artisan::call('email:send', [ - 'user' => 1, '--id' => [5, 13] + use Illuminate\Support\Facades\Artisan; + + Route::post('/mail', function () { + $exitCode = Artisan::call('mail:send', [ + '--id' => [5, 13] ]); }); <a name="passing-boolean-values"></a> #### Passing Boolean Values -If you need to specify the value of an option that does not accept string values, such as the `--force` flag on the `migrate:refresh` command, you should pass `true` or `false`: +If you need to specify the value of an option that does not accept string values, such as the `--force` flag on the `migrate:refresh` command, you should pass `true` or `false` as the value of the option: $exitCode = Artisan::call('migrate:refresh', [ '--force' => true, ]); +<a name="queueing-artisan-commands"></a> +#### Queueing Artisan Commands + +Using the `queue` method on the `Artisan` facade, you may even queue Artisan commands so they are processed in the background by your [queue workers](/docs/{{version}}/queues). Before using this method, make sure you have configured your queue and are running a queue listener: + + use Illuminate\Support\Facades\Artisan; + + Route::post('/user/{user}/mail', function ($user) { + Artisan::queue('mail:send', [ + 'user' => $user, '--queue' => 'default' + ]); + + // + }); + +Using the `onConnection` and `onQueue` methods, you may specify the connection or queue the Artisan command should be dispatched to: + + Artisan::queue('mail:send', [ + 'user' => 1, '--queue' => 'default' + ])->onConnection('redis')->onQueue('commands'); + <a name="calling-commands-from-other-commands"></a> ### Calling Commands From Other Commands @@ -530,7 +570,7 @@ Sometimes you may wish to call other commands from an existing Artisan command. */ public function handle() { - $this->call('email:send', [ + $this->call('mail:send', [ 'user' => 1, '--queue' => 'default' ]); @@ -539,7 +579,7 @@ Sometimes you may wish to call other commands from an existing Artisan command. If you would like to call another console command and suppress all of its output, you may use the `callSilent` method. The `callSilent` method has the same signature as the `call` method: - $this->callSilent('email:send', [ + $this->callSilent('mail:send', [ 'user' => 1, '--queue' => 'default' ]); From eda0b47bbdc94b5e8047c3f951d2f89ae624b5b4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 12:49:05 -0600 Subject: [PATCH 092/274] wip --- artisan.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/artisan.md b/artisan.md index 588afc31cb..76fd8a253a 100644 --- a/artisan.md +++ b/artisan.md @@ -561,7 +561,7 @@ Using the `onConnection` and `onQueue` methods, you may specify the connection o <a name="calling-commands-from-other-commands"></a> ### Calling Commands From Other Commands -Sometimes you may wish to call other commands from an existing Artisan command. You may do so using the `call` method. This `call` method accepts the command name and an array of command parameters: +Sometimes you may wish to call other commands from an existing Artisan command. You may do so using the `call` method. This `call` method accepts the command name and an array of command arguments / options: /** * Execute the console command. @@ -577,20 +577,20 @@ Sometimes you may wish to call other commands from an existing Artisan command. // } -If you would like to call another console command and suppress all of its output, you may use the `callSilent` method. The `callSilent` method has the same signature as the `call` method: +If you would like to call another console command and suppress all of its output, you may use the `callSilently` method. The `callSilently` method has the same signature as the `call` method: - $this->callSilent('mail:send', [ + $this->callSilently('mail:send', [ 'user' => 1, '--queue' => 'default' ]); <a name="stub-customization"></a> ## Stub Customization -The Artisan console's `make` commands are used to create a variety of classes, such as controllers, jobs, migrations, and tests. These classes are generated using "stub" files that are populated with values based on your input. However, you may sometimes wish to make small changes to files generated by Artisan. To accomplish this, you may use the `stub:publish` command to publish the most common stubs for customization: +The Artisan console's `make` commands are used to create a variety of classes, such as controllers, jobs, migrations, and tests. These classes are generated using "stub" files that are populated with values based on your input. However, you may want to to make small changes to files generated by Artisan. To accomplish this, you may use the `stub:publish` command to publish the most common stubs to your application so that you can customize them: php artisan stub:publish -The published stubs will be located within a `stubs` directory in the root of your application. Any changes you make to these stubs will be reflected when you generate their corresponding classes using Artisan `make` commands. +The published stubs will be located within a `stubs` directory in the root of your application. Any changes you make to these stubs will be reflected when you generate their corresponding classes using Artisan's `make` commands. <a name="events"></a> ## Events From afe3848ae273c198cb12df90322a8c2e90a4cdc2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 14:19:24 -0600 Subject: [PATCH 093/274] wip --- broadcasting.md | 152 +++++++++++++++++++++++++----------------------- 1 file changed, 78 insertions(+), 74 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 4bf3bf2879..68aa9ed5b9 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -1,8 +1,10 @@ # Broadcasting - [Introduction](#introduction) +- [Getting Started](#getting-started) - [Configuration](#configuration) - - [Driver Prerequisites](#driver-prerequisites) + - [Server Side Driver Installation](#driver-installation) + - [Laravel Echo Installation](#laravel-echo-installation) - [Concept Overview](#concept-overview) - [Using An Example Application](#using-example-application) - [Defining Broadcast Events](#defining-broadcast-events) @@ -17,7 +19,6 @@ - [Broadcasting Events](#broadcasting-events) - [Only To Others](#only-to-others) - [Receiving Broadcasts](#receiving-broadcasts) - - [Installing Laravel Echo](#installing-laravel-echo) - [Listening For Events](#listening-for-events) - [Leaving A Channel](#leaving-a-channel) - [Namespaces](#namespaces) @@ -31,70 +32,73 @@ <a name="introduction"></a> ## Introduction -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. +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. WebSockets provide a more efficient alternative to continually polling your application's server for data changes that should be reflected in your UI. -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. +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 and data between your server-side Laravel application and your client-side JavaScript application. -> {tip} Before diving into event broadcasting, make sure you have read all of the documentation regarding Laravel [events and listeners](/docs/{{version}}/events). +<a name="supported-drivers"></a> +#### Supported Drivers + +By default, Laravel includes two broadcasting drivers for you to choose from: [Pusher Channels](https://pusher.com/channels) and [Socket.io + Redis](https://socket.io). However, community driven packages such as [laravel-websockets](https://beyondco.de/docs/laravel-websockets/getting-started/introduction) provide additional broadcasting drivers that do not require commercial broadcasting providers. + +> {tip} Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). + +<a name="getting-started"></a> +## Getting Started + +To get started using Laravel's event broadcasting, we first need to do some configuration within the Laravel application as well as install a few packages. + +Event broadcasting is accomplished by a server side broadcasting driver that broadcasts your Laravel events so that Laravel Echo (a JavaScript library) can receive them within the browser client. Don't worry - we'll walk through each part of the installation process step-by-step. <a name="configuration"></a> ### Configuration -All of your application's event broadcasting configuration is stored in the `config/broadcasting.php` configuration file. Laravel supports several broadcast drivers out of the box: [Pusher Channels](https://pusher.com/channels), [Redis](/docs/{{version}}/redis), and a `log` driver for local development and debugging. Additionally, a `null` driver is included which allows you to totally disable broadcasting. A configuration example is included for each of these drivers in the `config/broadcasting.php` configuration file. +All of your application's event broadcasting configuration is stored in the `config/broadcasting.php` configuration file. Laravel supports several broadcast drivers out of the box: [Pusher Channels](https://pusher.com/channels), [Redis](/docs/{{version}}/redis), and a `log` driver for local development and debugging. Additionally, a `null` driver is included which allows you to totally disable broadcasting during testing. A configuration example is included for each of these drivers in the `config/broadcasting.php` configuration file. <a name="broadcast-service-provider"></a> #### Broadcast Service Provider -Before broadcasting any events, you will first need to register the `App\Providers\BroadcastServiceProvider`. In fresh Laravel applications, you only need to uncomment this provider in the `providers` array of your `config/app.php` configuration file. This provider will allow you to register the broadcast authorization routes and callbacks. +Before broadcasting any events, you will first need to register the `App\Providers\BroadcastServiceProvider`. In a new Laravel applications, you only need to uncomment this provider in the `providers` array of your `config/app.php` configuration file. This `BroadcastServiceProvider` contains the code necessary to register the broadcast authorization routes and callbacks. -<a name="csrf-token"></a> -#### CSRF Token +<a name="queue-configuration"></a> +#### Queue Configuration -[Laravel Echo](#installing-laravel-echo) will need access to the current session's CSRF token. You should verify that your application's `head` HTML element defines a `meta` tag containing the CSRF token: +You will also need to configure and run a [queue worker](/docs/{{version}}/queues). All event broadcasting is done via queued jobs so that the response time of your application is not seriously affected by events being broadcast. - <meta name="csrf-token" content="{{ csrf_token() }}"> - -<a name="driver-prerequisites"></a> -### Driver Prerequisites +<a name="driver-installation"></a> +### Server Side Driver Installation <a name="pusher-channels"></a> #### Pusher Channels -If you are broadcasting your events over [Pusher Channels](https://pusher.com/channels), you should install the Pusher Channels PHP SDK using the Composer package manager: +If you plan to broadcast your events using [Pusher Channels](https://pusher.com/channels), you should install the Pusher Channels PHP SDK using the Composer package manager: composer require pusher/pusher-php-server "~4.0" -Next, you should configure your Channels credentials in the `config/broadcasting.php` configuration file. An example Channels configuration is already included in this file, allowing you to quickly specify your Channels key, secret, and application ID. The `config/broadcasting.php` file's `pusher` configuration also allows you to specify additional `options` that are supported by Channels, such as the cluster: - - 'options' => [ - 'cluster' => 'eu', - 'useTLS' => true - ], - -When using Channels and [Laravel Echo](#installing-laravel-echo), you should specify `pusher` as your desired broadcaster when instantiating the Echo instance in your `resources/js/bootstrap.js` file: - - import Echo from "laravel-echo"; +Next, you should configure your Channels credentials in the `config/broadcasting.php` configuration file. An example Channels configuration is already included in this file, allowing you to quickly specify your Channels key, secret, and application ID. Typically, these values should be set via the `PUSHER_APP_KEY`, `PUSHER_APP_SECRET`, and `PUSHER_APP_ID` [environment variables](/docs/{{version}}/configuration#environment-configuration): - window.Pusher = require('pusher-js'); + PUSHER_APP_ID=your-pusher-app-id + PUSHER_APP_KEY=your-pusher-key + PUSHER_APP_SECRET=your-pusher-secret + PUSHER_APP_CLUSTER=mt1 - window.Echo = new Echo({ - broadcaster: 'pusher', - key: 'your-pusher-channels-key' - }); +The `config/broadcasting.php` file's `pusher` configuration also allows you to specify additional `options` that are supported by Channels, such as the cluster. -Finally, you will need to change your broadcast driver to `pusher` in your `.env` file: +Next, you will need to change your broadcast driver to `pusher` in your `.env` file: BROADCAST_DRIVER=pusher +Finally, you are ready to install and configure [Laravel Echo](#laravel-echo-installation), which will receive the broadcast events on the client-side. + <a name="pusher-compatible-laravel-websockets"></a> #### Pusher Compatible Laravel Websockets -The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) is a pure PHP, Pusher compatible websocket package for Laravel. This package allows you to leverage the full power of Laravel broadcasting without an external websocket provider or Node. For more information on installing and using this package, please consult its [official documentation](https://beyondco.de/docs/laravel-websockets). +The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) package is a pure PHP, Pusher compatible websocket package for Laravel. This package allows you to leverage the full power of Laravel broadcasting without an external websocket provider or Node. For more information on installing and using this package, please consult its [official documentation](https://beyondco.de/docs/laravel-websockets). <a name="redis"></a> #### Redis -If you are using the Redis broadcaster, you should either install the phpredis PHP extension via PECL or install the Predis library via Composer: +If you plan to use the Redis broadcaster, you should either install the phpredis PHP extension via PECL or install the Predis library via Composer: composer require predis/predis @@ -126,10 +130,48 @@ Next, you will need to instantiate Echo with the `socket.io` connector and a `ho 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. -<a name="queue-prerequisites"></a> -#### Queue Prerequisites +<a name="laravel-echo-installation"></a> +### Laravel Echo Installation -Before broadcasting events, you will also need to configure and run a [queue listener](/docs/{{version}}/queues). All event broadcasting is done via queued jobs so that the response time of your application is not seriously affected. +Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server side broadcasting driver. You may install Echo via the NPM package manager. In this example, we will also install the `pusher-js` package since we will be using the Pusher Channels broadcaster: + +```bash +npm install --save-dev laravel-echo pusher-js +``` + +Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the `resources/js/bootstrap.js` file that is included with the Laravel framework. By default, an example Echo configuration is already included in this file - you simply need to uncomment it: + + import Echo from 'laravel-echo'; + + window.Pusher = require('pusher-js'); + + window.Echo = new Echo({ + broadcaster: 'pusher', + key: process.env.MIX_PUSHER_APP_KEY, + cluster: process.env.MIX_PUSHER_APP_CLUSTER, + forceTLS: true + }); + +Once you have uncommented and adjusted the Echo configuration according to your needs, you may compile your application's assets: + + npm run dev + +> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Laravel Mix](/docs/{{version}}/mix). + +<a name="using-an-existing-client-instance"></a> +#### Using An Existing Client Instance + +If you already have a pre-configured Pusher Channels client instance that you would like Echo to utilize, you may pass it to Echo via the `client` configuration option: + + import Echo from 'laravel-echo'; + + const client = require('pusher-js'); + + window.Echo = new Echo({ + broadcaster: 'pusher', + key: 'your-pusher-channels-key', + client: client + }); <a name="concept-overview"></a> ## Concept Overview @@ -491,44 +533,6 @@ If you are not using Vue and Axios, you will need to manually configure your Jav <a name="receiving-broadcasts"></a> ## Receiving Broadcasts -<a name="installing-laravel-echo"></a> -### Installing Laravel Echo - -Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by Laravel. You may install Echo via the NPM package manager. In this example, we will also install the `pusher-js` package since we will be using the Pusher Channels broadcaster: - - npm install --save-dev laravel-echo pusher-js - -Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the `resources/js/bootstrap.js` file that is included with the Laravel framework: - - import Echo from "laravel-echo" - - window.Echo = new Echo({ - broadcaster: 'pusher', - key: 'your-pusher-channels-key' - }); - -When creating an Echo instance that uses the `pusher` connector, you may also specify a `cluster` as well as whether the connection must be made over TLS (by default, when `forceTLS` is `false`, a non-TLS connection will be made if the page was loaded over HTTP, or as a fallback if a TLS connection fails): - - window.Echo = new Echo({ - broadcaster: 'pusher', - key: 'your-pusher-channels-key', - cluster: 'eu', - forceTLS: true - }); - -<a name="using-an-existing-client-instance"></a> -#### Using An Existing Client Instance - -If you already have a Pusher Channels or Socket.io client instance that you would like Echo to utilize, you may pass it to Echo via the `client` configuration option: - - const client = require('pusher-js'); - - window.Echo = new Echo({ - broadcaster: 'pusher', - key: 'your-pusher-channels-key', - client: client - }); - <a name="listening-for-events"></a> ### Listening For Events From 721b53413b07ce9b3b6b467cce8c23e4a4506721 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 16:11:47 -0600 Subject: [PATCH 094/274] wip --- broadcasting.md | 134 +++++++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 53 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 68aa9ed5b9..b89384c99b 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -1,10 +1,13 @@ # Broadcasting - [Introduction](#introduction) -- [Getting Started](#getting-started) +- [Server Side Installation](#server-side-installation) - [Configuration](#configuration) - - [Server Side Driver Installation](#driver-installation) - - [Laravel Echo Installation](#laravel-echo-installation) + - [Pusher Channels](#pusher-channels) + - [Ably](#ably) +- [Client Side Installation](#client-side-installation) + - [Pusher Channels](#client-pusher-channels) + - [Ably](#client-ably) - [Concept Overview](#concept-overview) - [Using An Example Application](#using-example-application) - [Defining Broadcast Events](#defining-broadcast-events) @@ -34,21 +37,23 @@ 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. WebSockets provide a more efficient alternative to continually polling your application's server for data changes that should be reflected in your UI. -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 and data between your server-side Laravel application and your client-side JavaScript application. +For example, imagine your application is able to export a user's data to a CSV file and email it to them. However, creating this CSV file takes several minutes so you choose to create and mail the CSV within a [queued job](/docs/{{version}}/queues). When the CSV has been created and mailed to the user, we can use event broadcasting to dispatch a `App\Events\UserDataExported` event that is received by our application's JavaScript. Once the event is received, we can display a message to the user that their CSV has been emailed to them without them ever needing to refresh the page. + +To assist you in building these types of features, Laravel makes it easy to "broadcast" your server-side Laravel [events](/docs/{{version}}/events) over a WebSocket connection. Broadcasting your Laravel events allows you to share the same event names and data between your server-side Laravel application and your client-side JavaScript application. <a name="supported-drivers"></a> #### Supported Drivers -By default, Laravel includes two broadcasting drivers for you to choose from: [Pusher Channels](https://pusher.com/channels) and [Socket.io + Redis](https://socket.io). However, community driven packages such as [laravel-websockets](https://beyondco.de/docs/laravel-websockets/getting-started/introduction) provide additional broadcasting drivers that do not require commercial broadcasting providers. +By default, Laravel includes two server-side broadcasting drivers for you to choose from: [Pusher Channels](https://pusher.com/channels) and [Ably](https://ably.io). However, community driven packages such as [laravel-websockets](https://beyondco.de/docs/laravel-websockets/getting-started/introduction) provide additional broadcasting drivers that do not require commercial broadcasting providers. > {tip} Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). -<a name="getting-started"></a> -## Getting Started +<a name="server-side-installation"></a> +## Server Side Installation -To get started using Laravel's event broadcasting, we first need to do some configuration within the Laravel application as well as install a few packages. +To get started using Laravel's event broadcasting, we need to do some configuration within the Laravel application as well as install a few packages. -Event broadcasting is accomplished by a server side broadcasting driver that broadcasts your Laravel events so that Laravel Echo (a JavaScript library) can receive them within the browser client. Don't worry - we'll walk through each part of the installation process step-by-step. +Event broadcasting is accomplished by a server-side broadcasting driver that broadcasts your Laravel events so that Laravel Echo (a JavaScript library) can receive them within the browser client. Don't worry - we'll walk through each part of the installation process step-by-step. <a name="configuration"></a> ### Configuration @@ -65,17 +70,14 @@ Before broadcasting any events, you will first need to register the `App\Provide You will also need to configure and run a [queue worker](/docs/{{version}}/queues). All event broadcasting is done via queued jobs so that the response time of your application is not seriously affected by events being broadcast. -<a name="driver-installation"></a> -### Server Side Driver Installation - <a name="pusher-channels"></a> -#### Pusher Channels +### Pusher Channels If you plan to broadcast your events using [Pusher Channels](https://pusher.com/channels), you should install the Pusher Channels PHP SDK using the Composer package manager: composer require pusher/pusher-php-server "~4.0" -Next, you should configure your Channels credentials in the `config/broadcasting.php` configuration file. An example Channels configuration is already included in this file, allowing you to quickly specify your Channels key, secret, and application ID. Typically, these values should be set via the `PUSHER_APP_KEY`, `PUSHER_APP_SECRET`, and `PUSHER_APP_ID` [environment variables](/docs/{{version}}/configuration#environment-configuration): +Next, you should configure your Pusher Channels credentials in the `config/broadcasting.php` configuration file. An example Pusher Channels configuration is already included in this file, allowing you to quickly specify your key, secret, and application ID. Typically, these values should be set via the `PUSHER_APP_KEY`, `PUSHER_APP_SECRET`, and `PUSHER_APP_ID` [environment variables](/docs/{{version}}/configuration#environment-configuration): PUSHER_APP_ID=your-pusher-app-id PUSHER_APP_KEY=your-pusher-key @@ -88,52 +90,37 @@ Next, you will need to change your broadcast driver to `pusher` in your `.env` f BROADCAST_DRIVER=pusher -Finally, you are ready to install and configure [Laravel Echo](#laravel-echo-installation), which will receive the broadcast events on the client-side. +Finally, you are ready to install and configure [Laravel Echo](#client-side-installation), which will receive the broadcast events on the client-side. <a name="pusher-compatible-laravel-websockets"></a> #### Pusher Compatible Laravel Websockets The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) package is a pure PHP, Pusher compatible websocket package for Laravel. This package allows you to leverage the full power of Laravel broadcasting without an external websocket provider or Node. For more information on installing and using this package, please consult its [official documentation](https://beyondco.de/docs/laravel-websockets). -<a name="redis"></a> -#### Redis - -If you plan to use the Redis broadcaster, you should either install the phpredis PHP extension via PECL or install the Predis library via Composer: - - composer require predis/predis - -Next, you should update your broadcast driver to `redis` in your `.env` file: +<a name="ably"></a> +### Ably - BROADCAST_DRIVER=redis +If you plan to broadcast your events using [Ably](https://ably.io), you should install the Ably PHP SDK using the Composer package manager: -The Redis broadcaster will broadcast messages using Redis' pub / sub feature; however, you will need to pair this with a WebSocket server that can receive the messages from Redis and broadcast them to your WebSocket channels. + composer require ably/ably-php -When the Redis broadcaster publishes an event, it will be published on the event's specified channel names and the payload will be a JSON encoded string containing the event name, a `data` payload, and the user that generated the event's socket ID (if applicable). +Next, you should configure your Ably credentials in the `config/broadcasting.php` configuration file. An example Ably configuration is already included in this file, allowing you to quickly specify your key. Typically, this value should be set via the `ABLY_KEY` [environment variable](/docs/{{version}}/configuration#environment-configuration): -<a name="socketio"></a> -#### Socket.IO + ABLY_KEY=your-ably-key -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. You may install it via the NPM package manager: +Next, you will need to change your broadcast driver to `ably` in your `.env` file: - npm install --save-dev socket.io-client + BROADCAST_DRIVER=ably -Next, you will need to instantiate Echo with the `socket.io` connector and a `host`. - - import Echo from "laravel-echo" - - window.io = require('socket.io-client'); - - window.Echo = new Echo({ - broadcaster: 'socket.io', - host: window.location.hostname + ':6001' - }); +Finally, you are ready to install and configure [Laravel Echo](#client-side-installation), which will receive the broadcast events on the client-side. -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. +<a name="client-side-installation"></a> +## Client Side Installation -<a name="laravel-echo-installation"></a> -### Laravel Echo Installation +<a name="client-pusher-channels"></a> +### Pusher Channels -Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server side broadcasting driver. You may install Echo via the NPM package manager. In this example, we will also install the `pusher-js` package since we will be using the Pusher Channels broadcaster: +Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. You may install Echo via the NPM package manager. In this example, we will also install the `pusher-js` package since we will be using the Pusher Channels broadcaster: ```bash npm install --save-dev laravel-echo pusher-js @@ -173,10 +160,46 @@ If you already have a pre-configured Pusher Channels client instance that you wo client: client }); +<a name="client-ably"></a> +### Ably + +Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. You may install Echo via the NPM package manager. In this example, we will also install the `pusher-js` package. + +You may wonder why we would install the `pusher-js` JavaScript library even though we are using Ably to broadcast our events. Thankfully, Ably includes a Pusher compatibility mode which lets us use the Pusher protocol when listening for events in our client-side application: + +```bash +npm install --save-dev laravel-echo pusher-js +``` + +**Before continuing, you should enable Pusher protocol support in your Ably application settings. You may enable this feature within the "Protocol Adapter Settings" portion of your Ably application's settings dashboard.** + +Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the `resources/js/bootstrap.js` file that is included with the Laravel framework. By default, an example Echo configuration is already included in this file; however, the default configuration in the `bootstrap.js` file is intended for Pusher. You may copy the configuration below to transition your configuration to Ably: + + import Echo from 'laravel-echo'; + + window.Pusher = require('pusher-js'); + + window.Echo = new Echo({ + broadcaster: 'pusher', + key: process.env.MIX_ABLY_PUBLIC_KEY, + wsHost: 'realtime-pusher.ably.io', + wsPort: 443, + disableStats: true, + encrypted: true, + }); + +Note that our Ably Echo configuration references a `MIX_ABLY_PUBLIC_KEY` environment variable. This variable's value should be your Ably public key. Your public key is the portion of your Ably key that occurs before the `:` character. + +Once you have uncommented and adjusted the Echo configuration according to your needs, you may compile your application's assets: + + npm run dev + +> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Laravel Mix](/docs/{{version}}/mix). + <a name="concept-overview"></a> ## 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 Channels](https://pusher.com/channels) 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 Channels](https://pusher.com/channels) and [Ably](https://ably.io) 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. @@ -185,11 +208,13 @@ Events are broadcast over "channels", which may be specified as public or privat <a name="using-example-application"></a> ### 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 Channels](https://pusher.com/channels) or [Laravel Echo](#installing-laravel-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. 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: - event(new ShippingStatusUpdated($update)); + use App\Events\ShippingStatusUpdated; + + ShippingStatusUpdated::dispatch($shipment); <a name="the-shouldbroadcast-interface"></a> #### The `ShouldBroadcast` Interface @@ -200,6 +225,7 @@ When a user is viewing one of their orders, we don't want them to have to refres namespace App\Events; + use App\Shipment; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; @@ -210,11 +236,11 @@ 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. + * The shipment instance. * - * @var string + * @var \App\Shipment */ - public $update; + public $shipment; } 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: @@ -226,13 +252,15 @@ The `ShouldBroadcast` interface requires our event to define a `broadcastOn` met */ public function broadcastOn() { - return new PrivateChannel('order.'.$this->update->order_id); + return new PrivateChannel('order.'.$this->shipment->order_id); } <a name="example-application-authorizing-channels"></a> #### Authorizing Channels -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: +Remember, users must be authorized to listen on private channels. We may define our channel authorization rules in our application's `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: + + use App\Models\Order; Broadcast::channel('order.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; @@ -249,7 +277,7 @@ Next, all that remains is to listen for the event in our JavaScript application. Echo.private(`order.${orderId}`) .listen('ShippingStatusUpdated', (e) => { - console.log(e.update); + console.log(e.shipment); }); <a name="defining-broadcast-events"></a> From 8115b27e3ccb67aedb23e565e17a379f7d2573e5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 16:23:59 -0600 Subject: [PATCH 095/274] wip --- broadcasting.md | 50 +++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index b89384c99b..212a4f1688 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -210,22 +210,22 @@ Events are broadcast over "channels", which may be specified as public or privat Before diving into each component of event broadcasting, let's take a high level overview using an e-commerce store as an example. -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: +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 `OrderShipmentStatusUpdated` event is fired when a shipping status update is processed by the application: - use App\Events\ShippingStatusUpdated; + use App\Events\OrderShipmentStatusUpdated; - ShippingStatusUpdated::dispatch($shipment); + OrderShipmentStatusUpdated::dispatch($order); <a name="the-shouldbroadcast-interface"></a> #### The `ShouldBroadcast` Interface -When a user is viewing one of their orders, we don't want them to have to refresh the page to view status updates. Instead, we want to broadcast the updates to the application as they are created. So, we need to mark the `ShippingStatusUpdated` event with the `ShouldBroadcast` interface. This will instruct Laravel to broadcast the event when it is fired: +When a user is viewing one of their orders, we don't want them to have to refresh the page to view status updates. Instead, we want to broadcast the updates to the application as they are created. So, we need to mark the `OrderShipmentStatusUpdated` event with the `ShouldBroadcast` interface. This will instruct Laravel to broadcast the event when it is fired: <?php namespace App\Events; - use App\Shipment; + use App\Order; use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; @@ -233,14 +233,14 @@ When a user is viewing one of their orders, we don't want them to have to refres use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Queue\SerializesModels; - class ShippingStatusUpdated implements ShouldBroadcast + class OrderShipmentStatusUpdated implements ShouldBroadcast { /** - * The shipment instance. + * The order instance. * - * @var \App\Shipment + * @var \App\Order */ - public $shipment; + public $order; } 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: @@ -252,7 +252,7 @@ The `ShouldBroadcast` interface requires our event to define a `broadcastOn` met */ public function broadcastOn() { - return new PrivateChannel('order.'.$this->shipment->order_id); + return new PrivateChannel('order.'.$this->order->id); } <a name="example-application-authorizing-channels"></a> @@ -276,14 +276,14 @@ All authorization callbacks receive the currently authenticated user as their fi 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}`) - .listen('ShippingStatusUpdated', (e) => { - console.log(e.shipment); + .listen('OrderShipmentStatusUpdated', (e) => { + console.log(e.order); }); <a name="defining-broadcast-events"></a> ## Defining Broadcast Events -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. +To inform Laravel that a given event should be broadcast, you must 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 may subscribe to, while `PrivateChannels` and `PresenceChannels` represent private channels that require [channel authorization](#authorizing-channels): @@ -303,11 +303,17 @@ The `ShouldBroadcast` interface requires you to implement a single method: `broa { use SerializesModels; + /** + * The user that created the server. + * + * @var \App\Models\User + */ public $user; /** * Create a new event instance. * + * @param \App\Models\User $user * @return void */ public function __construct(User $user) @@ -326,7 +332,7 @@ The `ShouldBroadcast` interface requires you to implement a single method: `broa } } -Then, you only need to [fire the event](/docs/{{version}}/events) as you normally would. Once the event has been fired, a [queued job](/docs/{{version}}/queues) will automatically broadcast the event over your specified broadcast driver. +After implementing the `ShouldBroadcast` interface, you only need to [fire the event](/docs/{{version}}/events) as you normally would. Once the event has been fired, a [queued job](/docs/{{version}}/queues) will automatically broadcast the event using your specified broadcast driver. <a name="broadcast-name"></a> ### Broadcast Name @@ -377,14 +383,21 @@ However, if you wish to have more fine-grained control over your broadcast paylo <a name="broadcast-queue"></a> ### Broadcast Queue -By default, each broadcast event is placed on the default queue for the default queue connection specified in your `queue.php` configuration file. You may customize the queue used by the broadcaster by defining a `broadcastQueue` property on your event class. This property should specify the name of the queue you wish to use when broadcasting: +By default, each broadcast event is placed on the default queue for the default queue connection specified in your `queue.php` configuration file. You may customize the queue connection and name used by the broadcaster by defining `connection` and `queue` properties on your event class: + + /** + * The name of the queue connection to use when broadcasting the event. + * + * @var string + */ + public $connection = 'redis'; /** - * The name of the queue on which to place the event. + * The name of the queue on which to place the broadcasting job. * * @var string */ - public $broadcastQueue = 'your-queue-name'; + public $queue = 'default'; If you want to broadcast your event using the `sync` queue instead of the default queue driver, you can implement the `ShouldBroadcastNow` interface instead of `ShouldBroadcast`: @@ -396,6 +409,7 @@ If you want to broadcast your event using the `sync` queue instead of the defaul { // } + <a name="broadcast-conditions"></a> ### Broadcast Conditions @@ -408,7 +422,7 @@ Sometimes you want to broadcast your event only if a given condition is true. Yo */ public function broadcastWhen() { - return $this->value > 100; + return $this->order->value > 100; } <a name="authorizing-channels"></a> From 23b3b1c17327cbd13fc93cab383811fa5a19a006 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 16:35:27 -0600 Subject: [PATCH 096/274] wip --- broadcasting.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index 212a4f1688..f04b845ad0 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -433,7 +433,7 @@ Private channels require you to authorize that the currently authenticated user <a name="defining-authorization-routes"></a> ### Defining Authorization Routes -Thankfully, Laravel makes it easy to define the routes to respond to channel authorization requests. In the `BroadcastServiceProvider` included with your Laravel application, you will see a call to the `Broadcast::routes` method. This method will register the `/broadcasting/auth` route to handle authorization requests: +Thankfully, Laravel makes it easy to define the routes to respond to channel authorization requests. In the `App\Providers\BroadcastServiceProvider` included with your Laravel application, you will see a call to the `Broadcast::routes` method. This method will register the `/broadcasting/auth` route to handle authorization requests: Broadcast::routes(); @@ -448,14 +448,14 @@ By default, Echo will use the `/broadcasting/auth` endpoint to authorize channel window.Echo = new Echo({ broadcaster: 'pusher', - key: 'your-pusher-channels-key', + // ... authEndpoint: '/custom/endpoint/auth' }); <a name="defining-authorization-callbacks"></a> ### Defining 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: +Next, we need to define the logic that will actually determine if the currently authenticated user can listen to a given channel. 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.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; @@ -468,7 +468,7 @@ All authorization callbacks receive the currently authenticated user as their fi <a name="authorization-callback-model-binding"></a> #### 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: +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 a string or numeric order ID, you may request an actual `Order` model instance: use App\Models\Order; @@ -476,6 +476,8 @@ Just like HTTP routes, channel routes may also take advantage of implicit and ex return $user->id === $order->user_id; }); +> {note} Unlike HTTP route model binding, channel model binding does not support automatic [implicit model binding scoping](/docs/{{version}}/routing#implicit-model-binding-scoping). However, this is rarely a problem because most channels can be scoped based on a single model's unique, primary key. + <a name="authorization-callback-authentication"></a> #### Authorization Callback Authentication From ee8a1c58a24bdacea44509ca40df9887f9a9d15a Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 16:43:05 -0600 Subject: [PATCH 097/274] wip --- broadcasting.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index f04b845ad0..f1bcf16741 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -273,7 +273,7 @@ All authorization callbacks receive the currently authenticated user as their fi <a name="listening-for-event-broadcasts"></a> #### 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: +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 `OrderShipmentStatusUpdated` event. By default, all of the event's public properties will be included on the broadcast event: Echo.private(`order.${orderId}`) .listen('OrderShipmentStatusUpdated', (e) => { @@ -405,7 +405,7 @@ If you want to broadcast your event using the `sync` queue instead of the defaul use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; - class ShippingStatusUpdated implements ShouldBroadcastNow + class OrderShipmentStatusUpdated implements ShouldBroadcastNow { // } @@ -539,38 +539,38 @@ Finally, you may place the authorization logic for your channel in the channel c <a name="broadcasting-events"></a> ## Broadcasting Events -Once you have defined an event and marked it with the `ShouldBroadcast` interface, you only need to fire the event using the `event` function. The event dispatcher will notice that the event is marked with the `ShouldBroadcast` interface and will queue the event for broadcasting: +Once you have defined an event and marked it with the `ShouldBroadcast` interface, you only need to fire the event using the event's dispatch method. The event dispatcher will notice that the event is marked with the `ShouldBroadcast` interface and will queue the event for broadcasting: - event(new ShippingStatusUpdated($update)); + use App\Events\OrderShipmentStatusUpdated; + + OrderShipmentStatusUpdated::dispatch($order)); <a name="only-to-others"></a> ### Only To Others -When building an application that utilizes event broadcasting, you may substitute the `event` function with the `broadcast` function. Like the `event` function, the `broadcast` function dispatches the event to your server-side listeners: +When building an application that utilizes event broadcasting, you may occasionally need to broadcast an event to all subscribers to a given channel except for the current user. You may accomplish this using the `broadcast` helper and the `toOthers` method: - broadcast(new ShippingStatusUpdated($update)); - -However, the `broadcast` function also exposes the `toOthers` method which allows you to exclude the current user from the broadcast's recipients: + use App\Events\OrderShipmentStatusUpdated; - broadcast(new ShippingStatusUpdated($update))->toOthers(); + broadcast(new OrderShipmentStatusUpdated($update))->toOthers(); -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: +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` URL 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: axios.post('/task', task) .then((response) => { this.tasks.push(response.data); }); -However, remember that we also broadcast the task's creation. If your JavaScript application is listening for this event in order to add tasks to the task list, you will have duplicate tasks in your list: one from the end-point and one from the broadcast. You may solve this by using the `toOthers` method to instruct the broadcaster to not broadcast the event to the current user. +However, remember that we also broadcast the task's creation. If your JavaScript application is also listening for this event in order to add tasks to the task list, you will have duplicate tasks in your list: one from the end-point and one from the broadcast. You may solve this by using the `toOthers` method to instruct the broadcaster to not broadcast the event to the current user. > {note} Your event must use the `Illuminate\Broadcasting\InteractsWithSockets` trait in order to call the `toOthers` method. <a name="only-to-others-configuration"></a> #### 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 [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. +When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. If you are using a global [Axios](https://github.com/mzabriskie/axios) instance to make HTTP requests from your JavaScript application, 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 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: +If you are not using a global Axios instance, you will need to manually configure your JavaScript application to send the `X-Socket-ID` header with all outgoing requests. You may retrieve the socket ID using the `Echo.socketId` method: var socketId = Echo.socketId(); From 39b9327fcb22c5c8a7433b64baaa084c1043d123 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 17:04:03 -0600 Subject: [PATCH 098/274] wip --- broadcasting.md | 147 +++++++++++++++++++++++++++--------------------- 1 file changed, 83 insertions(+), 64 deletions(-) diff --git a/broadcasting.md b/broadcasting.md index f1bcf16741..f689e4486a 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -128,16 +128,18 @@ npm install --save-dev laravel-echo pusher-js Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the `resources/js/bootstrap.js` file that is included with the Laravel framework. By default, an example Echo configuration is already included in this file - you simply need to uncomment it: - import Echo from 'laravel-echo'; +```js +import Echo from 'laravel-echo'; - window.Pusher = require('pusher-js'); +window.Pusher = require('pusher-js'); - window.Echo = new Echo({ - broadcaster: 'pusher', - key: process.env.MIX_PUSHER_APP_KEY, - cluster: process.env.MIX_PUSHER_APP_CLUSTER, - forceTLS: true - }); +window.Echo = new Echo({ + broadcaster: 'pusher', + key: process.env.MIX_PUSHER_APP_KEY, + cluster: process.env.MIX_PUSHER_APP_CLUSTER, + forceTLS: true +}); +``` Once you have uncommented and adjusted the Echo configuration according to your needs, you may compile your application's assets: @@ -150,15 +152,17 @@ Once you have uncommented and adjusted the Echo configuration according to your If you already have a pre-configured Pusher Channels client instance that you would like Echo to utilize, you may pass it to Echo via the `client` configuration option: - import Echo from 'laravel-echo'; +```js +import Echo from 'laravel-echo'; - const client = require('pusher-js'); +const client = require('pusher-js'); - window.Echo = new Echo({ - broadcaster: 'pusher', - key: 'your-pusher-channels-key', - client: client - }); +window.Echo = new Echo({ + broadcaster: 'pusher', + key: 'your-pusher-channels-key', + client: client +}); +``` <a name="client-ably"></a> ### Ably @@ -175,18 +179,20 @@ npm install --save-dev laravel-echo pusher-js Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the `resources/js/bootstrap.js` file that is included with the Laravel framework. By default, an example Echo configuration is already included in this file; however, the default configuration in the `bootstrap.js` file is intended for Pusher. You may copy the configuration below to transition your configuration to Ably: - import Echo from 'laravel-echo'; +```js +import Echo from 'laravel-echo'; - window.Pusher = require('pusher-js'); +window.Pusher = require('pusher-js'); - window.Echo = new Echo({ - broadcaster: 'pusher', - key: process.env.MIX_ABLY_PUBLIC_KEY, - wsHost: 'realtime-pusher.ably.io', - wsPort: 443, - disableStats: true, - encrypted: true, - }); +window.Echo = new Echo({ + broadcaster: 'pusher', + key: process.env.MIX_ABLY_PUBLIC_KEY, + wsHost: 'realtime-pusher.ably.io', + wsPort: 443, + disableStats: true, + encrypted: true, +}); +``` Note that our Ably Echo configuration references a `MIX_ABLY_PUBLIC_KEY` environment variable. This variable's value should be your Ably public key. Your public key is the portion of your Ably key that occurs before the `:` character. @@ -252,7 +258,7 @@ The `ShouldBroadcast` interface requires our event to define a `broadcastOn` met */ public function broadcastOn() { - return new PrivateChannel('order.'.$this->order->id); + return new PrivateChannel('orders.'.$this->order->id); } <a name="example-application-authorizing-channels"></a> @@ -262,7 +268,7 @@ Remember, users must be authorized to listen on private channels. We may define use App\Models\Order; - Broadcast::channel('order.{orderId}', function ($user, $orderId) { + Broadcast::channel('orders.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; }); @@ -275,10 +281,12 @@ All authorization callbacks receive the currently authenticated user as their fi 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 `OrderShipmentStatusUpdated` event. By default, all of the event's public properties will be included on the broadcast event: - Echo.private(`order.${orderId}`) - .listen('OrderShipmentStatusUpdated', (e) => { - console.log(e.order); - }); +```js +Echo.private(`orders.${orderId}`) + .listen('OrderShipmentStatusUpdated', (e) => { + console.log(e.order); + }); +``` <a name="defining-broadcast-events"></a> ## Defining Broadcast Events @@ -457,7 +465,7 @@ By default, Echo will use the `/broadcasting/auth` endpoint to authorize channel Next, we need to define the logic that will actually determine if the currently authenticated user can listen to a given channel. 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.{orderId}', function ($user, $orderId) { + Broadcast::channel('orders.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; }); @@ -472,7 +480,7 @@ Just like HTTP routes, channel routes may also take advantage of implicit and ex use App\Models\Order; - Broadcast::channel('order.{order}', function ($user, Order $order) { + Broadcast::channel('orders.{order}', function ($user, Order $order) { return $user->id === $order->user_id; }); @@ -498,7 +506,7 @@ Next, register your channel in your `routes/channels.php` file: use App\Broadcasting\OrderChannel; - Broadcast::channel('order.{order}', OrderChannel::class); + Broadcast::channel('orders.{order}', OrderChannel::class); Finally, you may place the authorization logic for your channel in the channel class' `join` method. This `join` method will house the same logic you would have typically placed in your channel authorization closure. You may also take advantage of channel model binding: @@ -580,53 +588,64 @@ If you are not using a global Axios instance, you will need to manually configur <a name="listening-for-events"></a> ### Listening For Events -Once you have installed and instantiated Echo, you are ready to start listening for event broadcasts. First, use the `channel` method to retrieve an instance of a channel, then call the `listen` method to listen for a specified event: +Once you have [installed and instantiated Laravel Echo](#client-side-installation), you are ready to start listening for events that are broadcast from your Laravel application. First, use the `channel` method to retrieve an instance of a channel, then call the `listen` method to listen for a specified event: - Echo.channel('orders') - .listen('OrderShipped', (e) => { - console.log(e.order.name); - }); +```js +Echo.channel(`orders.${this.order.id}`) + .listen('OrderShipmentStatusUpdated', (e) => { + console.log(e.order.name); + }); +``` If you would like to listen for events on a private channel, use the `private` method instead. You may continue to chain calls to the `listen` method to listen for multiple events on a single channel: - Echo.private('orders') - .listen(...) - .listen(...) - .listen(...); +```js +Echo.private(`orders.${this.order.id}`) + .listen(...) + .listen(...) + .listen(...); +``` <a name="leaving-a-channel"></a> ### Leaving A Channel To leave a channel, you may call the `leaveChannel` method on your Echo instance: - Echo.leaveChannel('orders'); +```js +Echo.leaveChannel(`orders.${this.order.id}`); +``` If you would like to leave a channel and also its associated private and presence channels, you may call the `leave` method: - Echo.leave('orders'); - +```js +Echo.leave(`orders.${this.order.id}`); +``` <a name="namespaces"></a> ### Namespaces -You may have noticed in the examples above that we did not specify the full namespace for the event classes. This is because Echo will automatically assume the events are located in the `App\Events` namespace. However, you may configure the root namespace when you instantiate Echo by passing a `namespace` configuration option: +You may have noticed in the examples above that we did not specify the full `App\Events` namespace for the event classes. This is because Echo will automatically assume the events are located in the `App\Events` namespace. However, you may configure the root namespace when you instantiate Echo by passing a `namespace` configuration option: - window.Echo = new Echo({ - broadcaster: 'pusher', - key: 'your-pusher-channels-key', - namespace: 'App.Other.Namespace' - }); +```js +window.Echo = new Echo({ + broadcaster: 'pusher', + // ... + namespace: 'App.Other.Namespace' +}); +``` Alternatively, you may prefix event classes with a `.` when subscribing to them using Echo. This will allow you to always specify the fully-qualified class name: - Echo.channel('orders') - .listen('.Namespace\\Event\\Class', (e) => { - // - }); +```js +Echo.channel('orders') + .listen('.Namespace\\Event\\Class', (e) => { + // + }); +``` <a name="presence-channels"></a> ## Presence Channels -Presence channels build on the security of private channels while exposing the additional feature of awareness of who is subscribed to the channel. This makes it easy to build powerful, collaborative application features such as notifying users when another user is viewing the same page. +Presence channels build on the security of private channels while exposing the additional feature of awareness of who is subscribed to the channel. This makes it easy to build powerful, collaborative application features such as notifying users when another user is viewing the same page or listing the inhabitants of a chat room. <a name="authorizing-presence-channels"></a> ### Authorizing Presence Channels @@ -674,13 +693,13 @@ Presence channels may receive events just like public or private channels. Using return new PresenceChannel('room.'.$this->message->room_id); } -Like public or private events, presence channel events may be broadcast using the `broadcast` function. As with other events, you may use the `toOthers` method to exclude the current user from receiving the broadcast: +As with other events, you may use the `broadcast` helper and the `toOthers` method to exclude the current user from receiving the broadcast: broadcast(new NewMessage($message)); broadcast(new NewMessage($message))->toOthers(); -You may listen for the join event via Echo's `listen` method: +As typical of other types of events, you may listen for events sent to presence channels using Echo's `listen` method: Echo.join(`chat.${roomId}`) .here(...) @@ -699,14 +718,14 @@ Sometimes you may wish to broadcast an event to other connected clients without To broadcast client events, you may use Echo's `whisper` method: - Echo.private('chat') + Echo.private(`chat.${roomId}`) .whisper('typing', { name: this.user.name }); To listen for client events, you may use the `listenForWhisper` method: - Echo.private('chat') + Echo.private(`chat.${roomId}`) .listenForWhisper('typing', (e) => { console.log(e.name); }); @@ -714,13 +733,13 @@ To listen for client events, you may use the `listenForWhisper` method: <a name="notifications"></a> ## Notifications -By pairing event broadcasting with [notifications](/docs/{{version}}/notifications), your JavaScript application may receive new notifications as they occur without needing to refresh the page. First, be sure to read over the documentation on using [the broadcast notification channel](/docs/{{version}}/notifications#broadcast-notifications). +By pairing event broadcasting with [notifications](/docs/{{version}}/notifications), your JavaScript application may receive new notifications as they occur without needing to refresh the page. Before getting started, be sure to read over the documentation on using [the broadcast notification channel](/docs/{{version}}/notifications#broadcast-notifications). 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.Models.User.${userId}`) .notification((notification) => { console.log(notification.type); }); -In this example, all notifications sent to `App\Models\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. +In this example, all notifications sent to `App\Models\User` instances via the `broadcast` channel would be received by the callback. A channel authorization callback for the `App.Models.User.{id}` channel is included in the default `BroadcastServiceProvider` that ships with the Laravel framework. From 811b0b418d2a1869509faf958d18509755818281 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Tue, 17 Nov 2020 21:39:20 -0600 Subject: [PATCH 099/274] wip --- broadcasting.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/broadcasting.md b/broadcasting.md index f689e4486a..1e9acacb67 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -5,6 +5,7 @@ - [Configuration](#configuration) - [Pusher Channels](#pusher-channels) - [Ably](#ably) + - [Open Source Alternatives](#open-source-alternatives) - [Client Side Installation](#client-side-installation) - [Pusher Channels](#client-pusher-channels) - [Ably](#client-ably) @@ -95,7 +96,7 @@ Finally, you are ready to install and configure [Laravel Echo](#client-side-inst <a name="pusher-compatible-laravel-websockets"></a> #### Pusher Compatible Laravel Websockets -The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) package is a pure PHP, Pusher compatible websocket package for Laravel. This package allows you to leverage the full power of Laravel broadcasting without an external websocket provider or Node. For more information on installing and using this package, please consult its [official documentation](https://beyondco.de/docs/laravel-websockets). +The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) package is a pure PHP, Pusher compatible WebSocket package for Laravel. This package allows you to leverage the full power of Laravel broadcasting without a commercial WebSocket provider. For more information on installing and using this package, please consult its [official documentation](https://beyondco.de/docs/laravel-websockets). <a name="ably"></a> ### Ably @@ -114,6 +115,11 @@ Next, you will need to change your broadcast driver to `ably` in your `.env` fil Finally, you are ready to install and configure [Laravel Echo](#client-side-installation), which will receive the broadcast events on the client-side. +<a name="open-source-alternatives"></a> +### Open Source Alternatives + +The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) package is a pure PHP, Pusher compatible WebSocket package for Laravel. This package allows you to leverage the full power of Laravel broadcasting without a commercial WebSocket provider. For more information on installing and using this package, please consult its [official documentation](https://beyondco.de/docs/laravel-websockets). + <a name="client-side-installation"></a> ## Client Side Installation From 801886ec74223f146cb1a16fdb113c57ffca2838 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 10:23:06 -0600 Subject: [PATCH 100/274] wip --- cache.md | 56 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/cache.md b/cache.md index 69a2bfc8ba..8742d11c96 100644 --- a/cache.md +++ b/cache.md @@ -1,5 +1,6 @@ # Cache +- [Introduction](#introduction) - [Configuration](#configuration) - [Driver Prerequisites](#driver-prerequisites) - [Cache Usage](#cache-usage) @@ -21,12 +22,19 @@ - [Registering The Driver](#registering-the-driver) - [Events](#events) +<a name="introduction"></a> +## Introduction + +Some of the data retrieval or processing tasks performed by your application could be CPU intensive or take several seconds to complete. When this is the case, it is common to cache the retrieved data for a time so it can retrieved quickly on subsequent requests for the same data. The cached data is usually stored in a very fast data store such as [Memcached](https://memcached.org) or [Redis](https://redis.io). + +Thankfully, Laravel provides an expressive, unified API for various cache backends, allowing you to take advantage of their blazing fast data retrieval and speed up your web application. + <a name="configuration"></a> ## 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 to be used by default throughout your application. Laravel supports popular caching backends like [Memcached](https://memcached.org) and [Redis](https://redis.io) out of the box. +Your application's cache configuration file is located at `config/cache.php`. In this file you may specify which cache driver you would like to be used by default throughout your application. Laravel supports popular caching backends like [Memcached](https://memcached.org) and [Redis](https://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. +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 on the server's 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. <a name="driver-prerequisites"></a> ### Driver Prerequisites @@ -47,17 +55,19 @@ When using the `database` cache driver, you will need to setup a table to contai <a name="memcached"></a> #### Memcached -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: +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. This file already contains a `memcached.servers` entry to get you started: 'memcached' => [ - [ - 'host' => '127.0.0.1', - 'port' => 11211, - 'weight' => 100 + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], ], ], -You may also set the `host` option to a UNIX socket path. If you do this, the `port` option should be set to `0`: +If needed, you may set the `host` option to a UNIX socket path. If you do this, the `port` option should be set to `0`: 'memcached' => [ [ @@ -70,7 +80,7 @@ You may also set the `host` option to a UNIX socket path. If you do this, the `p <a name="redis"></a> #### Redis -Before using a Redis cache with Laravel, you will need to either install the PhpRedis PHP extension via PECL or install the `predis/predis` package (~1.0) via Composer. +Before using a Redis cache with Laravel, you will need to either install the PhpRedis PHP extension via PECL or install the `predis/predis` package (~1.0) via Composer. [Laravel Sail](/docs/{{version}}/installation#laravel-sail) already includes this extension. In addition, official Laravel deployment platforms such as [Laravel Forge](https://forge.laravel.com) and [Laravel Vapor](https://vapor.laravel.com) have the PhpRedis extension installed by default. For more information on configuring Redis, consult its [Laravel documentation page](/docs/{{version}}/redis#configuration). @@ -80,9 +90,7 @@ For more information on configuring Redis, consult its [Laravel documentation pa <a name="obtaining-a-cache-instance"></a> ### Obtaining A Cache Instance -The `Illuminate\Contracts\Cache\Factory` and `Illuminate\Contracts\Cache\Repository` [contracts](/docs/{{version}}/contracts) provide access to Laravel's cache services. The `Factory` contract provides access to all cache drivers defined for your application. The `Repository` contract is typically an implementation of the default cache driver for your application as specified by your `cache` configuration file. - -However, you may also use the `Cache` facade, which is what we will use throughout this documentation. The `Cache` facade provides convenient, terse access to the underlying implementations of the Laravel cache contracts: +To obtain a cache store instance, you may use the `Cache` facade, which is what we will use throughout this documentation. The `Cache` facade provides convenient, terse access to the underlying implementations of the Laravel cache contracts: <?php @@ -117,7 +125,7 @@ Using the `Cache` facade, you may access various cache stores via the `store` me <a name="retrieving-items-from-the-cache"></a> ### Retrieving Items From The Cache -The `get` method on the `Cache` facade is used to retrieve items from the cache. If the item does not exist in the cache, `null` will be returned. If you wish, you may pass a second argument to the `get` method specifying the default value you wish to be returned if the item doesn't exist: +The `Cache` facade's `get` method is used to retrieve items from the cache. If the item does not exist in the cache, `null` will be returned. If you wish, you may pass a second argument to the `get` method specifying the default value you wish to be returned if the item doesn't exist: $value = Cache::get('key'); @@ -132,7 +140,7 @@ You may even pass a closure as the default value. The result of the closure will <a name="checking-for-item-existence"></a> #### Checking For Item Existence -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`: +The `has` method may be used to determine if an item exists in the cache. This method will also return `false` if the item exists but its value is `null`: if (Cache::has('key')) { // @@ -159,7 +167,7 @@ Sometimes you may wish to retrieve an item from the cache, but also store a defa If the item does not exist in the cache, the closure passed to the `remember` method will be executed and its result will be placed in the cache. -You may use the `rememberForever` method to retrieve an item from the cache or store it forever: +You may use the `rememberForever` method to retrieve an item from the cache or store it forever if it does not exist: $value = Cache::rememberForever('users', function () { return DB::table('users')->get(); @@ -177,20 +185,20 @@ If you need to retrieve an item from the cache and then delete the item, you may You may use the `put` method on the `Cache` facade to store items in the cache: - Cache::put('key', 'value', $seconds); + Cache::put('key', 'value', $seconds = 10); If the storage time is not passed to the `put` method, the item will be stored indefinitely: Cache::put('key', 'value'); -Instead of passing the number of seconds as an integer, you may also pass a `DateTime` instance representing the expiration time of the cached item: +Instead of passing the number of seconds as an integer, you may also pass a `DateTime` instance representing the desired expiration time of the cached item: Cache::put('key', 'value', now()->addMinutes(10)); <a name="store-if-not-present"></a> #### Store If Not Present -The `add` method will only add the item to the cache if it does not already exist in the cache store. The method will return `true` if the item is actually added to the cache. Otherwise, the method will return `false`: +The `add` method will only add the item to the cache if it does not already exist in the cache store. The method will return `true` if the item is actually added to the cache. Otherwise, the method will return `false`. The `add` method is an atomic operation: Cache::add('key', 'value', $seconds); @@ -210,7 +218,7 @@ You may remove items from the cache using the `forget` method: Cache::forget('key'); -You may also remove items by providing a zero or negative TTL: +You may also remove items by providing a zero or negative number of expiration seconds: Cache::put('key', 'value', 0); @@ -220,12 +228,12 @@ You may clear the entire cache using the `flush` method: Cache::flush(); -> {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. +> {note} Flushing the cache does not respect your configured cache "prefix" and will remove all entries from the cache. Consider this carefully when clearing a cache which is shared by other applications. <a name="the-cache-helper"></a> ### 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: +In addition to using the `Cache` facade, 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'); @@ -241,7 +249,7 @@ When the `cache` function is called without any arguments, it returns an instanc return DB::table('users')->get(); }); -> {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). +> {tip} When testing call to the global `cache` function, you may use the `Cache::shouldReceive` method just as if you were [testing the facade](/docs/{{version}}/mocking#mocking-facades). <a name="cache-tags"></a> ## Cache Tags @@ -251,7 +259,7 @@ When the `cache` function is called without any arguments, it returns an instanc <a name="storing-tagged-cache-items"></a> ### Storing Tagged Cache Items -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 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` a value into the cache: Cache::tags(['people', 'artists'])->put('John', $john, $seconds); @@ -273,7 +281,7 @@ You may flush all items that are assigned a tag or list of tags. For example, th Cache::tags(['people', 'authors'])->flush(); -In contrast, this statement would remove only caches tagged with `authors`, so `Anne` would be removed, but not `John`: +In contrast, this statement would remove only cached values tagged with `authors`, so `Anne` would be removed, but not `John`: Cache::tags('authors')->flush(); From e7f3dd6d19cc23a07bf487a5b919a1d32a0b0700 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 10:31:44 -0600 Subject: [PATCH 101/274] wip --- cache.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/cache.md b/cache.md index fef4638fab..259bf9d9d2 100644 --- a/cache.md +++ b/cache.md @@ -296,7 +296,7 @@ In contrast, this statement would remove only cached values tagged with `authors <a name="atomic-locks-prerequisites-database"></a> #### Database -When using the `database` cache driver, you will need to setup a table to contain the cache locks. You'll find an example `Schema` declaration for the table below: +When using the `database` cache driver, you will need to setup a table to contain your application's cache locks. You'll find an example `Schema` declaration for the table below: Schema::create('cache_locks', function ($table) { $table->string('key')->primary(); @@ -341,6 +341,8 @@ If the lock is not available at the moment you request it, you may instruct Lara optional($lock)->release(); } +The example above may be simplified by passing a closure to the `block` method. When a closure is passed to this method, Laravel will attempt to acquire the lock for the specified number of seconds and will automatically release the lock once the closure has been executed: + Cache::lock('foo', 10)->block(5, function () { // Lock acquired after waiting maximum of 5 seconds... }); @@ -348,23 +350,25 @@ If the lock is not available at the moment you request it, you may instruct Lara <a name="managing-locks-across-processes"></a> ### Managing Locks Across Processes -Sometimes, you may wish to acquire a lock in one process and release it in another process. For example, you may acquire a lock during a web request and wish to release the lock at the end of a queued job that is triggered by that request. In this scenario, you should pass the lock's scoped "owner token" to the queued job so that the job can re-instantiate the lock using the given token: +Sometimes, you may wish to acquire a lock in one process and release it in another process. For example, you may acquire a lock during a web request and wish to release the lock at the end of a queued job that is triggered by that request. In this scenario, you should pass the lock's scoped "owner token" to the queued job so that the job can re-instantiate the lock using the given token. + +In the example below, we will dispatch a queued job if a lock is successfully acquired. In addition, we will pass the lock's owner token to the queued job via the lock's `owner` method: - // Within Controller... $podcast = Podcast::find($id); - $lock = Cache::lock('foo', 120); + $lock = Cache::lock('processing', 120); if ($result = $lock->get()) { ProcessPodcast::dispatch($podcast, $lock->owner()); } - // Within ProcessPodcast Job... - Cache::restoreLock('foo', $this->owner)->release(); +Within our application's `ProcessPodcast` job, we can restore and release the lock using the owner token: + + Cache::restoreLock('processing', $this->owner)->release(); If you would like to release a lock without respecting its current owner, you may use the `forceRelease` method: - Cache::lock('foo')->forceRelease(); + Cache::lock('processing')->forceRelease(); <a name="adding-custom-cache-drivers"></a> ## Adding Custom Cache Drivers @@ -372,7 +376,7 @@ If you would like to release a lock without respecting its current owner, you ma <a name="writing-the-driver"></a> ### Writing The Driver -To create our custom cache driver, we first need to implement the `Illuminate\Contracts\Cache\Store` [contract](/docs/{{version}}/contracts). So, a MongoDB cache implementation would look something like this: +To create our custom cache driver, we first need to implement the `Illuminate\Contracts\Cache\Store` [contract](/docs/{{version}}/contracts). So, a MongoDB cache implementation might look something like this: <?php @@ -394,7 +398,7 @@ To create our custom cache driver, we first need to implement the `Illuminate\Co public function getPrefix() {} } -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. +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 [Laravel framework source code](https://github.com/laravel/framework). Once our implementation is complete, we can finish our custom driver registration by calling the `Cache` facade's `extend` method: Cache::extend('mongo', function ($app) { return Cache::repository(new MongoStore); @@ -447,7 +451,7 @@ Once your extension is registered, update your `config/cache.php` configuration <a name="events"></a> ## Events -To execute code on every cache operation, you may listen for the [events](/docs/{{version}}/events) fired by the cache. Typically, you should place these event listeners within your `EventServiceProvider`: +To execute code on every cache operation, you may listen for the [events](/docs/{{version}}/events) fired by the cache. Typically, you should place these event listeners within your application's `App\Providers\EventServiceProvider` class: /** * The event listener mappings for the application. From aef96136c5ebbc0d7f9a8ec7b9a96e91104d8a14 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 10:39:20 -0600 Subject: [PATCH 102/274] wip --- collections.md | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/collections.md b/collections.md index 1eef15cce5..b1ab331de7 100644 --- a/collections.md +++ b/collections.md @@ -18,8 +18,7 @@ The `Illuminate\Support\Collection` class provides a fluent, convenient wrapper $collection = collect(['taylor', 'abigail', null])->map(function ($name) { return strtoupper($name); - }) - ->reject(function ($name) { + })->reject(function ($name) { return empty($name); }); @@ -37,7 +36,7 @@ As mentioned above, the `collect` helper returns a new `Illuminate\Support\Colle <a name="extending-collections"></a> ### Extending Collections -Collections are "macroable", which allows you to add additional methods to the `Collection` class at run time. For example, the following code adds a `toUpper` method to the `Collection` class: +Collections are "macroable", which allows you to add additional methods to the `Collection` class at run time. The `Illuminate\Support\Collection` class' `macro` method accepts a closure that will be executed when your macro is called. The macro closure may access the collection's other methods via `$this`, just as if it were a real method of the collection class. For example, the following code adds a `toUpper` method to the `Collection` class: use Illuminate\Support\Collection; use Illuminate\Support\Str; @@ -54,12 +53,31 @@ Collections are "macroable", which allows you to add additional methods to the ` // ['FIRST', 'SECOND'] -Typically, you should declare collection macros in a [service provider](/docs/{{version}}/providers). +Typically, you should declare collection macros in the `boot` method of a [service provider](/docs/{{version}}/providers). + +<a name="macro-arguments"></a> +#### Macro Arguments + +If necessary, you may define macros that accept additional arguments: + + use Illuminate\Support\Collection; + use Illuminate\Support\Facades\Lang; + use Illuminate\Support\Str; + + Collection::macro('toLocale', function ($locale) { + return $this->map(function ($value) use ($locale) { + return Lang::get($value, $locale); + }); + }); + + $collection = collect(['first', 'second']); + + $translated = $collection->toLocale('es'); <a name="available-methods"></a> ## Available Methods -For the remainder of this documentation, we'll discuss each method available on the `Collection` class. Remember, all of these methods may be chained to fluently manipulate the underlying array. Furthermore, almost every method returns a new `Collection` instance, allowing you to preserve the original copy of the collection when necessary: +For the majority of the remaining collection documentation, we'll discuss each method available on the `Collection` class. Remember, all of these methods may be chained to fluently manipulate the underlying array. Furthermore, almost every method returns a new `Collection` instance, allowing you to preserve the original copy of the collection when necessary: <style> #collection-method-list > p { From 08c4c9e0cc54a8fdf8c1197ef46c2dd32e8d13e8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 11:50:59 -0600 Subject: [PATCH 103/274] wip --- collections.md | 298 ++++++++++++++++++++++++++++--------------------- 1 file changed, 171 insertions(+), 127 deletions(-) diff --git a/collections.md b/collections.md index b1ab331de7..54f0be065f 100644 --- a/collections.md +++ b/collections.md @@ -252,7 +252,12 @@ Alias for the [`avg`](#method-avg) method. The `avg` method returns the [average value](https://en.wikipedia.org/wiki/Average) of a given key: - $average = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->avg('foo'); + $average = collect([ + ['foo' => 10], + ['foo' => 10], + ['foo' => 20], + ['foo' => 40] + ])->avg('foo'); // 20 @@ -273,7 +278,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](https://getbootstrap.com/docs/4.1/layout/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/docs/4.1/layout/grid/). For example, imagine you have a collection of [Eloquent](/docs/{{version}}/eloquent) models you want to display in a grid: @foreach ($products->chunk(3) as $chunk) <div class="row"> @@ -286,12 +291,12 @@ This method is especially useful in [views](/docs/{{version}}/views) when workin <a name="method-chunkwhile"></a> #### `chunkWhile()` {#collection-method} -The `chunkWhile` method breaks the collection into multiple, smaller collections based on the evaluation of the given callback: +The `chunkWhile` method breaks the collection into multiple, smaller collections based on the evaluation of the given callback. The `$chunk` variable passed to the closure may be used to inspect the previous element: $collection = collect(str_split('AABBCCCD')); - $chunks = $collection->chunkWhile(function ($current, $key, $chunk) { - return $current === $chunk->last(); + $chunks = $collection->chunkWhile(function ($value, $key, $chunk) { + return $value === $chunk->last(); }); $chunks->all(); @@ -303,7 +308,11 @@ The `chunkWhile` method breaks the collection into multiple, smaller collections The `collapse` method collapses a collection of arrays into a single, flat collection: - $collection = collect([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); + $collection = collect([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ]); $collapsed = $collection->collapse(); @@ -360,7 +369,7 @@ The `collect` method is primarily useful for converting [lazy collections](#lazy <a name="method-concat"></a> #### `concat()` {#collection-method} -The `concat` method appends the given `array` or collection values onto the end of the collection: +The `concat` method appends the given `array` or collection's values onto the end of another collection: $collection = collect(['John Doe']); @@ -373,7 +382,17 @@ The `concat` method appends the given `array` or collection values onto the end <a name="method-contains"></a> #### `contains()` {#collection-method} -The `contains` method determines whether the collection contains a given item: +You may also pass a closure to the `contains` to determine if an element exists in the collection matching a given truth test: + + $collection = collect([1, 2, 3, 4, 5]); + + $collection->contains(function ($value, $key) { + return $value > 5; + }); + + // false + +Alternatively, you may pass a string to the `contains` method to determine whether the collection contains a given item value: $collection = collect(['name' => 'Desk', 'price' => 100]); @@ -396,16 +415,6 @@ You may also pass a key / value pair to the `contains` method, which will determ // false -Finally, you may also pass a callback to the `contains` method to perform your own truth test: - - $collection = collect([1, 2, 3, 4, 5]); - - $collection->contains(function ($value, $key) { - return $value > 5; - }); - - // false - The `contains` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`containsStrict`](#method-containsstrict) method to filter using "strict" comparisons. <a name="method-containsstrict"></a> @@ -429,7 +438,7 @@ The `count` method returns the total number of items in the collection: <a name="method-countBy"></a> #### `countBy()` {#collection-method} -The `countBy` method counts the occurrences of values in the collection. By default, the method counts the occurrences of every element: +The `countBy` method counts the occurrences of values in the collection. By default, the method counts the occurrences of every element, allowing you to count certain "types" of elements in the collection: $collection = collect([1, 2, 2, 2, 3]); @@ -439,7 +448,7 @@ The `countBy` method counts the occurrences of values in the collection. By defa // [1 => 1, 2 => 3, 3 => 1] -However, you pass a callback to the `countBy` method to count all items by a custom value: +You pass a closure to the `countBy` method to count all items by a custom value: $collection = collect(['alice@gmail.com', 'bob@yahoo.com', 'carlos@gmail.com']); @@ -622,16 +631,16 @@ This method has the same signature as the [`duplicates`](#method-duplicates) met <a name="method-each"></a> #### `each()` {#collection-method} -The `each` method iterates over the items in the collection and passes each item to a callback: +The `each` method iterates over the items in the collection and passes each item to a closure: $collection->each(function ($item, $key) { // }); -If you would like to stop iterating through the items, you may return `false` from your callback: +If you would like to stop iterating through the items, you may return `false` from your closure: $collection->each(function ($item, $key) { - if (/* some condition */) { + if (/* condition */) { return false; } }); @@ -664,7 +673,7 @@ The `every` method may be used to verify that all elements of a collection pass // false -If the collection is empty, `every` will return true: +If the collection is empty, the `every` method will return true: $collection = collect([]); @@ -749,7 +758,7 @@ The `firstWhere` method returns the first element in the collection with the giv // ['name' => 'Linda', 'age' => 14] -You may also call the `firstWhere` method with an operator: +You may also call the `firstWhere` method with a comparison operator: $collection->firstWhere('age', '>=', 18); @@ -764,7 +773,7 @@ Like the [where](#method-where) method, you may pass one argument to the `firstW <a name="method-flatmap"></a> #### `flatMap()` {#collection-method} -The `flatMap` method iterates through the collection and passes each value to the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items. Then, the array is flattened by a level: +The `flatMap` method iterates through the collection and passes each value to the given closure. The closure is free to modify the item and return it, thus forming a new collection of modified items. Then, the array is flattened by one level: $collection = collect([ ['name' => 'Sally'], @@ -785,7 +794,12 @@ The `flatMap` method iterates through the collection and passes each value to th The `flatten` method flattens a multi-dimensional collection into a single dimension: - $collection = collect(['name' => 'taylor', 'languages' => ['php', 'javascript']]); + $collection = collect([ + 'name' => 'taylor', + 'languages' => [ + 'php', 'javascript' + ] + ]); $flattened = $collection->flatten(); @@ -793,14 +807,20 @@ The `flatten` method flattens a multi-dimensional collection into a single dimen // ['taylor', 'php', 'javascript']; -You may optionally pass the function a "depth" argument: +If necessary, you may pass the `flatten` method a "depth" argument: $collection = collect([ 'Apple' => [ - ['name' => 'iPhone 6S', 'brand' => 'Apple'], + [ + 'name' => 'iPhone 6S', + 'brand' => 'Apple' + ], ], 'Samsung' => [ - ['name' => 'Galaxy S7', 'brand' => 'Samsung'], + [ + 'name' => 'Galaxy S7', + 'brand' => 'Samsung' + ], ], ]); @@ -815,7 +835,7 @@ You may optionally pass the function a "depth" argument: ] */ -In this example, calling `flatten` without providing the depth would have also flattened the nested arrays, resulting in `['iPhone 6S', 'Apple', 'Galaxy S7', 'Samsung']`. Providing a depth allows you to restrict the levels of nested arrays that will be flattened. +In this example, calling `flatten` without providing the depth would have also flattened the nested arrays, resulting in `['iPhone 6S', 'Apple', 'Galaxy S7', 'Samsung']`. Providing a depth allows you to specify the number of levels nested arrays will be flattened. <a name="method-flip"></a> #### `flip()` {#collection-method} @@ -873,17 +893,17 @@ You may optionally pass a default value as the second argument: $collection = collect(['name' => 'taylor', 'framework' => 'laravel']); - $value = $collection->get('foo', 'default-value'); + $value = $collection->get('age', 34); - // default-value + // 34 -You may even pass a callback as the default value. The result of the callback will be returned if the specified key does not exist: +You may even pass a callback as the method's default value. The result of the callback will be returned if the specified key does not exist: $collection->get('email', function () { - return 'default-value'; + return 'taylor@example.com'; }); - // default-value + // taylor@example.com <a name="method-groupby"></a> #### `groupBy()` {#collection-method} @@ -941,12 +961,9 @@ Multiple grouping criteria may be passed as an array. Each array element will be 40 => ['user' => 4, 'skill' => 2, 'roles' => ['Role_2']], ]); - $result = $data->groupBy([ - 'skill', - function ($item) { - return $item['roles']; - }, - ], $preserveKeys = true); + $result = $data->groupBy(['skill', function ($item) { + return $item['roles']; + }], $preserveKeys = true); /* [ @@ -995,7 +1012,7 @@ The `has` method determines if a given key exists in the collection: <a name="method-implode"></a> #### `implode()` {#collection-method} -The `implode` method joins the items in a collection. Its arguments depend on the type of items in the collection. If the collection contains arrays or objects, you should pass the key of the attributes you wish to join, and the "glue" string you wish to place between the values: +The `implode` method joins items in a collection. Its arguments depend on the type of items in the collection. If the collection contains arrays or objects, you should pass the key of the attributes you wish to join, and the "glue" string you wish to place between the values: $collection = collect([ ['account_id' => 1, 'product' => 'Desk'], @@ -1006,7 +1023,7 @@ The `implode` method joins the items in a collection. Its arguments depend on th // Desk, Chair -If the collection contains simple strings or numeric values, pass the "glue" as the only argument to the method: +If the collection contains simple strings or numeric values, you should pass the "glue" as the only argument to the method: collect([1, 2, 3, 4, 5])->implode('-'); @@ -1030,7 +1047,7 @@ The `intersect` method removes any values from the original collection that are <a name="method-intersectbykeys"></a> #### `intersectByKeys()` {#collection-method} -The `intersectByKeys` method removes any keys from the original collection that are not present in the given `array` or collection: +The `intersectByKeys` method removes any keys and their corresponding values from the original collection that are not present in the given `array` or collection: $collection = collect([ 'serial' => 'UX301', 'type' => 'screen', 'year' => 2009, @@ -1065,7 +1082,7 @@ The `isNotEmpty` method returns `true` if the collection is not empty; otherwise <a name="method-join"></a> #### `join()` {#collection-method} -The `join` method joins the collection's values with a string: +The `join` method joins the collection's values with a string. Using this method's second argument, you may also specify how the final element should be appended to the string: collect(['a', 'b', 'c'])->join(', '); // 'a, b, c' collect(['a', 'b', 'c'])->join(', ', ', and '); // 'a, b, and c' @@ -1199,7 +1216,7 @@ The `mapInto()` method iterates over the collection, creating a new instance of <a name="method-mapspread"></a> #### `mapSpread()` {#collection-method} -The `mapSpread` method iterates over the collection's items, passing each nested item value into the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items: +The `mapSpread` method iterates over the collection's items, passing each nested item value into the given closure. The closure is free to modify the item and return it, thus forming a new collection of modified items: $collection = collect([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); @@ -1216,7 +1233,7 @@ The `mapSpread` method iterates over the collection's items, passing each nested <a name="method-maptogroups"></a> #### `mapToGroups()` {#collection-method} -The `mapToGroups` method groups the collection's items by the given callback. The callback should return an associative array containing a single key / value pair, thus forming a new collection of grouped values: +The `mapToGroups` method groups the collection's items by the given closure. The closure should return an associative array containing a single key / value pair, thus forming a new collection of grouped values: $collection = collect([ [ @@ -1286,7 +1303,10 @@ The `mapWithKeys` method iterates through the collection and passes each value t The `max` method returns the maximum value of a given key: - $max = collect([['foo' => 10], ['foo' => 20]])->max('foo'); + $max = collect([ + ['foo' => 10], + ['foo' => 20] + ])->max('foo'); // 20 @@ -1299,7 +1319,12 @@ The `max` method returns the maximum value of a given key: The `median` method returns the [median value](https://en.wikipedia.org/wiki/Median) of a given key: - $median = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->median('foo'); + $median = collect([ + ['foo' => 10], + ['foo' => 10], + ['foo' => 20], + ['foo' => 40] + ])->median('foo'); // 15 @@ -1337,7 +1362,11 @@ The `mergeRecursive` method merges the given array or collection recursively wit $collection = collect(['product_id' => 1, 'price' => 100]); - $merged = $collection->mergeRecursive(['product_id' => 2, 'price' => 200, 'discount' => false]); + $merged = $collection->mergeRecursive([ + 'product_id' => 2, + 'price' => 200, + 'discount' => false + ]); $merged->all(); @@ -1361,7 +1390,12 @@ The `min` method returns the minimum value of a given key: The `mode` method returns the [mode value](https://en.wikipedia.org/wiki/Mode_(statistics)) of a given key: - $mode = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->mode('foo'); + $mode = collect([ + ['foo' => 10], + ['foo' => 10], + ['foo' => 20], + ['foo' => 40] + ])->mode('foo'); // [10] @@ -1380,7 +1414,7 @@ The `nth` method creates a new collection consisting of every n-th element: // ['a', 'e'] -You may optionally pass an offset as the second argument: +You may optionally pass a starting offset as the second argument: $collection->nth(4, 1); @@ -1391,7 +1425,12 @@ You may optionally pass an offset as the second argument: The `only` method returns the items in the collection with the specified keys: - $collection = collect(['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false]); + $collection = collect([ + 'product_id' => 1, + 'name' => 'Desk', + 'price' => 100, + 'discount' => false + ]); $filtered = $collection->only(['product_id', 'name']); @@ -1446,7 +1485,7 @@ The `partition` method may be combined with the `list` PHP function to separate <a name="method-pipe"></a> #### `pipe()` {#collection-method} -The `pipe` method passes the collection to the given callback and returns the result: +The `pipe` method passes the collection to the given closure and returns the result of the executed closure: $collection = collect([1, 2, 3]); @@ -1572,7 +1611,7 @@ The `prepend` method adds an item to the beginning of the collection: // [0, 1, 2, 3, 4, 5] -You may also pass a second argument to set the key of the prepended item: +You may also pass a second argument to specify the key of the prepended item: $collection = collect(['one' => 1, 'two' => 2]); @@ -1634,7 +1673,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. A collection of items is always returned when explicitly passing the number of items you wish to receive: +You may 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); @@ -1642,7 +1681,7 @@ You may optionally pass an integer to `random` to specify how many items you wou // [2, 4, 5] - (retrieved randomly) -If the Collection has fewer items than requested, the method will throw an `InvalidArgumentException`. +If the collection instance has fewer items than requested, the `random` method will throw an `InvalidArgumentException`. <a name="method-reduce"></a> #### `reduce()` {#collection-method} @@ -1668,7 +1707,7 @@ The value for `$carry` on the first iteration is `null`; however, you may specif <a name="method-reject"></a> #### `reject()` {#collection-method} -The `reject` method filters the collection using the given callback. The callback should return `true` if the item should be removed from the resulting collection: +The `reject` method filters the collection using the given closure. The closure should return `true` if the item should be removed from the resulting collection: $collection = collect([1, 2, 3, 4]); @@ -1685,7 +1724,7 @@ For the inverse of the `reject` method, see the [`filter`](#method-filter) metho <a name="method-replace"></a> #### `replace()` {#collection-method} -The `replace` method behaves similarly to `merge`; however, in addition to overwriting matching items with string keys, the `replace` method will also overwrite items in the collection that have matching numeric keys: +The `replace` method behaves similarly to `merge`; however, in addition to overwriting matching items that have string keys, the `replace` method will also overwrite items in the collection that have matching numeric keys: $collection = collect(['Taylor', 'Abigail', 'James']); @@ -1700,9 +1739,20 @@ The `replace` method behaves similarly to `merge`; however, in addition to overw This method works like `replace`, but it will recur into arrays and apply the same replacement process to the inner values: - $collection = collect(['Taylor', 'Abigail', ['James', 'Victoria', 'Finn']]); + $collection = collect([ + 'Taylor', + 'Abigail', + [ + 'James', + 'Victoria', + 'Finn' + ] + ]); - $replaced = $collection->replaceRecursive(['Charlie', 2 => [1 => 'King']]); + $replaced = $collection->replaceRecursive([ + 'Charlie', + 2 => [1 => 'King'] + ]); $replaced->all(); @@ -1732,7 +1782,7 @@ The `reverse` method reverses the order of the collection's items, preserving th <a name="method-search"></a> #### `search()` {#collection-method} -The `search` method searches the collection for the given value and returns its key if found. If the item is not found, `false` is returned. +The `search` method searches the collection for the given value and returns its key if found. If the item is not found, `false` is returned: $collection = collect([2, 4, 6, 8]); @@ -1742,13 +1792,13 @@ The `search` method searches the collection for the given value and returns its The search is done using a "loose" comparison, meaning a string with an integer value will be considered equal to an integer of the same value. To use "strict" comparison, pass `true` as the second argument to the method: - $collection->search('4', true); + collect([2, 4, 6, 8])->search('4', $strict = true); // false -Alternatively, you may pass in your own callback to search for the first item that passes your truth test: +Alternatively, you may provide your own closure to search for the first item that passes a given truth test: - $collection->search(function ($item, $key) { + collect([2, 4, 6, 8])->search(function ($item, $key) { return $item > 5; }); @@ -1785,7 +1835,7 @@ The `shuffle` method randomly shuffles the items in the collection: <a name="method-skip"></a> #### `skip()` {#collection-method} -The `skip` method returns a new collection, without the first given amount of items: +The `skip` method returns a new collection, with the given number of elements removed from the beginning of the collection: $collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); @@ -1798,7 +1848,7 @@ The `skip` method returns a new collection, without the first given amount of it <a name="method-skipuntil"></a> #### `skipUntil()` {#collection-method} -The `skipUntil` method skips items until the given callback returns `true` and then returns the remaining items in the collection: +The `skipUntil` method skips over items from the collection until the given callback returns `true` and then returns the remaining items in the collection as a new collection instance: $collection = collect([1, 2, 3, 4]); @@ -1825,7 +1875,7 @@ You may also pass a simple value to the `skipUntil` method to skip all items unt <a name="method-skipwhile"></a> #### `skipWhile()` {#collection-method} -The `skipWhile` method skips items while the given callback returns `true` and then returns the remaining items in the collection: +The `skipWhile` method skips over items from the collection while the given callback returns `true` and then returns the remaining items in the collection as a new collection: $collection = collect([1, 2, 3, 4]); @@ -1870,7 +1920,7 @@ Alias for the [`contains`](#method-contains) method. <a name="method-sort"></a> #### `sort()` {#collection-method} -The `sort` method sorts the collection. The sorted collection keeps the original array keys, so in this example we'll use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: +The `sort` method sorts the collection. The sorted collection keeps the original array keys, so in the following example we will use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: $collection = collect([5, 3, 1, 2, 4]); @@ -1880,14 +1930,14 @@ 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 [`uasort`](https://secure.php.net/manual/en/function.uasort.php#refsect1-function.uasort-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 [`uasort`](https://secure.php.net/manual/en/function.uasort.php#refsect1-function.uasort-parameters), which is what the collection's `sort` method calls utilizes internally. > {tip} If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. <a name="method-sortby"></a> #### `sortBy()` {#collection-method} -The `sortBy` method sorts the collection by the given key. The sorted collection keeps the original array keys, so in this example we'll use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: +The `sortBy` method sorts the collection by the given key. The sorted collection keeps the original array keys, so in the following example we will use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: $collection = collect([ ['name' => 'Desk', 'price' => 200], @@ -1907,7 +1957,7 @@ The `sortBy` method sorts the collection by the given key. The sorted collection ] */ -This method accepts [sort flags](https://www.php.net/manual/en/function.sort.php) as its second argument: +The `sort` method accepts [sort flags](https://www.php.net/manual/en/function.sort.php) as its second argument: $collection = collect([ ['title' => 'Item 1'], @@ -1927,7 +1977,7 @@ This method accepts [sort flags](https://www.php.net/manual/en/function.sort.php ] */ -Alternatively, you may pass your own callback to determine how to sort the collection's values: +Alternatively, you may pass your own closure to determine how to sort the collection's values: $collection = collect([ ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']], @@ -1967,7 +2017,7 @@ This method will sort the collection in the opposite order as the [`sort`](#meth // [5, 4, 3, 2, 1] -Unlike `sort`, you may not pass a callback to `sortDesc`. If you wish to use a callback, you should use [`sort`](#method-sort) and invert your comparison. +Unlike `sort`, you may not pass a closure to `sortDesc`. Instead, you should use the [`sort`](#method-sort) method and invert your comparison. <a name="method-sortkeys"></a> #### `sortKeys()` {#collection-method} @@ -2014,7 +2064,7 @@ The `splice` method removes and returns a slice of items starting at the specifi // [1, 2] -You may pass a second argument to limit the size of the resulting chunk: +You may pass a second argument to limit the size of the resulting collection: $collection = collect([1, 2, 3, 4, 5]); @@ -2028,7 +2078,7 @@ You may pass a second argument to limit the size of the resulting chunk: // [1, 2, 4, 5] -In addition, you can pass a third argument containing the new items to replace the items removed from the collection: +In addition, you may pass a third argument containing the new items to replace the items removed from the collection: $collection = collect([1, 2, 3, 4, 5]); @@ -2064,7 +2114,7 @@ The `sum` method returns the sum of all items in the collection: // 15 -If the collection contains nested arrays or objects, you should pass a key to use for determining which values to sum: +If the collection contains nested arrays or objects, you should pass a key that will be used to determine which values to sum: $collection = collect([ ['name' => 'JavaScript: The Good Parts', 'pages' => 176], @@ -2075,7 +2125,7 @@ If the collection contains nested arrays or objects, you should pass a key to us // 1272 -In addition, you may pass your own callback to determine which values of the collection to sum: +In addition, you may pass your own closure to determine which values of the collection to sum: $collection = collect([ ['name' => 'Chair', 'colors' => ['Black']], @@ -2102,7 +2152,7 @@ The `take` method returns a new collection with the specified number of items: // [0, 1, 2] -You may also pass a negative integer to take the specified amount of items from the end of the collection: +You may also pass a negative integer to take the specified number of items from the end of the collection: $collection = collect([0, 1, 2, 3, 4, 5]); @@ -2159,7 +2209,7 @@ The `takeWhile` method returns items in the collection until the given callback <a name="method-tap"></a> #### `tap()` {#collection-method} -The `tap` method passes the collection to the given callback, allowing you to "tap" into the collection at a specific point and do something with the items while not affecting the collection itself: +The `tap` method passes the collection to the given callback, allowing you to "tap" into the collection at a specific point and do something with the items while not affecting the collection itself. The collection is then returned by the `tap` method: collect([2, 4, 3, 1, 5]) ->sort() @@ -2173,7 +2223,7 @@ The `tap` method passes the collection to the given callback, allowing you to "t <a name="method-times"></a> #### `times()` {#collection-method} -The static `times` method creates a new collection by invoking the callback a given amount of times: +The static `times` method creates a new collection by invoking the given closure a specified number of times: $collection = Collection::times(10, function ($number) { return $number * 9; @@ -2183,22 +2233,6 @@ The static `times` method creates a new collection by invoking the callback a gi // [9, 18, 27, 36, 45, 54, 63, 72, 81, 90] -This method can be useful when combined with factories to create [Eloquent](/docs/{{version}}/eloquent) models: - - $categories = Collection::times(3, function ($number) { - return Category::factory()->create(['name' => "Category No. $number"]); - }); - - $categories->all(); - - /* - [ - ['id' => 1, 'name' => 'Category No. 1'], - ['id' => 2, 'name' => 'Category No. 2'], - ['id' => 3, 'name' => 'Category No. 3'], - ] - */ - <a name="method-toarray"></a> #### `toArray()` {#collection-method} @@ -2214,7 +2248,7 @@ The `toArray` method converts the collection into a plain PHP `array`. If the co ] */ -> {note} `toArray` also converts all of the collection's nested objects that are an instance of `Arrayable` to an array. If you want to get the raw underlying array, use the [`all`](#method-all) method instead. +> {note} `toArray` also converts all of the collection's nested objects that are an instance of `Arrayable` to an array. If you want to get the raw array underlying the collection, use the [`all`](#method-all) method instead. <a name="method-tojson"></a> #### `toJson()` {#collection-method} @@ -2260,7 +2294,7 @@ The `union` method adds the given array to the collection. If the given array co <a name="method-unique"></a> #### `unique()` {#collection-method} -The `unique` method returns all of the unique items in the collection. The returned collection keeps the original array keys, so in this example we'll use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: +The `unique` method returns all of the unique items in the collection. The returned collection keeps the original array keys, so in the following example we will use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: $collection = collect([1, 1, 2, 2, 3, 4, 2]); @@ -2291,7 +2325,7 @@ When dealing with nested arrays or objects, you may specify the key used to dete ] */ -You may also pass your own callback to determine item uniqueness: +Finally, you may also pass your own closure to the `unique` method to specify which value should determine an item's uniqueness: $unique = $collection->unique(function ($item) { return $item['brand'].$item['type']; @@ -2412,39 +2446,40 @@ For the inverse of `when`, see the [`unless`](#method-unless) method. The `whenEmpty` method will execute the given callback when the collection is empty: - $collection = collect(['michael', 'tom']); + $collection = collect(['Michael', 'Tom']); $collection->whenEmpty(function ($collection) { - return $collection->push('adam'); + return $collection->push('Adam'); }); $collection->all(); - // ['michael', 'tom'] + // ['Michael', 'Tom'] $collection = collect(); $collection->whenEmpty(function ($collection) { - return $collection->push('adam'); + return $collection->push('Adam'); }); $collection->all(); - // ['adam'] + // ['Adam'] +A second closure may be passed to the `whenEmpty` method that will be executed when the collection is not empty: - $collection = collect(['michael', 'tom']); + $collection = collect(['Michael', 'Tom']); $collection->whenEmpty(function ($collection) { - return $collection->push('adam'); + return $collection->push('Adam'); }, function ($collection) { - return $collection->push('taylor'); + return $collection->push('Taylor'); }); $collection->all(); - // ['michael', 'tom', 'taylor'] + // ['Michael', 'Tom', 'Taylor'] For the inverse of `whenEmpty`, see the [`whenNotEmpty`](#method-whennotempty) method. @@ -2474,6 +2509,7 @@ The `whenNotEmpty` method will execute the given callback when the collection is // [] +A second closure may be passed to the `whenNotEmpty` method that will be executed when the collection is empty: $collection = collect(); @@ -2541,7 +2577,7 @@ This method has the same signature as the [`where`](#method-where) method; howev <a name="method-wherebetween"></a> #### `whereBetween()` {#collection-method} -The `whereBetween` method filters the collection within a given range: +The `whereBetween` method filters the collection by determining if a specified item value is within a given range: $collection = collect([ ['product' => 'Desk', 'price' => 200], @@ -2566,7 +2602,7 @@ The `whereBetween` method filters the collection within a given range: <a name="method-wherein"></a> #### `whereIn()` {#collection-method} -The `whereIn` method filters the collection by a given key / value contained within the given array: +The `whereIn` method removes elements from the collection that do not have a specified item value that is contained within the given array: $collection = collect([ ['product' => 'Desk', 'price' => 200], @@ -2616,7 +2652,7 @@ The `whereInstanceOf` method filters the collection by a given class type: <a name="method-wherenotbetween"></a> #### `whereNotBetween()` {#collection-method} -The `whereNotBetween` method filters the collection within a given range: +The `whereNotBetween` method filters the collection by determining if a specified item value is outside of a given range: $collection = collect([ ['product' => 'Desk', 'price' => 200], @@ -2640,7 +2676,7 @@ The `whereNotBetween` method filters the collection within a given range: <a name="method-wherenotin"></a> #### `whereNotIn()` {#collection-method} -The `whereNotIn` method filters the collection by a given key / value not contained within the given array: +The `whereNotIn` method removes elements from the collection that have a specified item value that is not contained within the given array: $collection = collect([ ['product' => 'Desk', 'price' => 200], @@ -2670,7 +2706,7 @@ This method has the same signature as the [`whereNotIn`](#method-wherenotin) met <a name="method-wherenotnull"></a> #### `whereNotNull()` {#collection-method} -The `whereNotNull` method filters items where the given key is not null: +The `whereNotNull` method removes items from the collection where the given key is not `null`: $collection = collect([ ['name' => 'Desk'], @@ -2692,7 +2728,7 @@ The `whereNotNull` method filters items where the given key is not null: <a name="method-wherenull"></a> #### `whereNull()` {#collection-method} -The `whereNull` method filters items where the given key is null: +The `whereNull` method removes items from the collection where the given key is `null`: $collection = collect([ ['name' => 'Desk'], @@ -2716,6 +2752,8 @@ The `whereNull` method filters items where the given key is null: The static `wrap` method wraps the given value in a collection when applicable: + use Illuminate\Support\Collection; + $collection = Collection::wrap('John Doe'); $collection->all(); @@ -2737,7 +2775,7 @@ The static `wrap` method wraps the given value in a collection when applicable: <a name="method-zip"></a> #### `zip()` {#collection-method} -The `zip` method merges together the values of the given array with the values of the original collection at the corresponding index: +The `zip` method merges together the values of the given array with the values of the original collection at their corresponding index: $collection = collect(['Chair', 'Desk']); @@ -2750,10 +2788,12 @@ The `zip` method merges together the values of the given array with the values o <a name="higher-order-messages"></a> ## 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: [`average`](#method-average), [`avg`](#method-avg), [`contains`](#method-contains), [`each`](#method-each), [`every`](#method-every), [`filter`](#method-filter), [`first`](#method-first), [`flatMap`](#method-flatmap), [`groupBy`](#method-groupby), [`keyBy`](#method-keyby), [`map`](#method-map), [`max`](#method-max), [`min`](#method-min), [`partition`](#method-partition), [`reject`](#method-reject), [`skipUntil`](#method-skipuntil), [`skipWhile`](#method-skipwhile), [`some`](#method-some), [`sortBy`](#method-sortby), [`sortByDesc`](#method-sortbydesc), [`sum`](#method-sum), [`takeUntil`](#method-takeuntil), [`takeWhile`](#method-takewhile) and [`unique`](#method-unique). +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: [`average`](#method-average), [`avg`](#method-avg), [`contains`](#method-contains), [`each`](#method-each), [`every`](#method-every), [`filter`](#method-filter), [`first`](#method-first), [`flatMap`](#method-flatmap), [`groupBy`](#method-groupby), [`keyBy`](#method-keyby), [`map`](#method-map), [`max`](#method-max), [`min`](#method-min), [`partition`](#method-partition), [`reject`](#method-reject), [`skipUntil`](#method-skipuntil), [`skipWhile`](#method-skipwhile), [`some`](#method-some), [`sortBy`](#method-sortby), [`sortByDesc`](#method-sortbydesc), [`sum`](#method-sum), [`takeUntil`](#method-takeuntil), [`takeWhile`](#method-takewhile), and [`unique`](#method-unique). 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: + use App\Models\User; + $users = User::where('votes', '>', 500)->get(); $users->each->markAsVip(); @@ -2793,13 +2833,17 @@ For example, imagine your application needs to process a multi-gigabyte log file Or, imagine you need to iterate through 10,000 Eloquent models. When using traditional Laravel collections, all 10,000 Eloquent models must be loaded into memory at the same time: - $users = App\Models\User::all()->filter(function ($user) { + use App\Models\User; + + $users = User::all()->filter(function ($user) { return $user->id > 500; }); However, the query builder's `cursor` method returns a `LazyCollection` instance. This allows you to still only run a single query against the database but also only keep one Eloquent model loaded in memory at a time. In this example, the `filter` callback is not executed until we actually iterate over each user individually, allowing for a drastic reduction in memory usage: - $users = App\Models\User::cursor()->filter(function ($user) { + use App\Models\User; + + $users = User::cursor()->filter(function ($user) { return $user->id > 500; }); @@ -2940,7 +2984,7 @@ Almost all methods available on the `Collection` class are also available on the </div> -> {note} Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are _not_ available on the `LazyCollection` class. +> {note} Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are **not** available on the `LazyCollection` class. <a name="lazy-collection-methods"></a> ### Lazy Collection Methods @@ -2967,16 +3011,16 @@ While the `each` method calls the given callback for each item in the collection <a name="method-remember"></a> #### `remember()` {#collection-method} -The `remember` method returns a new lazy collection that will remember any values that have already been enumerated and will not retrieve them again when the collection is enumerated again: - - $users = User::cursor()->remember(); +The `remember` method returns a new lazy collection that will remember any values that have already been enumerated and will not retrieve them again on subsequent collection enumerations: // No query has been executed yet... + $users = User::cursor()->remember(); + // The query is executed... + // The first 5 users are hydrated from the database... $users->take(5)->all(); - // The query has been executed and the first 5 users have been hydrated from the database... - + // First 5 users come from the collection's cache... + // The rest are hydrated from the database... $users->take(20)->all(); - // First 5 users come from the collection's cache... The rest are hydrated from the database... From 7670c0bb3121368c7cccd345e8c14d31e81ebad5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 11:51:52 -0600 Subject: [PATCH 104/274] wip --- collections.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/collections.md b/collections.md index 54f0be065f..750df31b44 100644 --- a/collections.md +++ b/collections.md @@ -2996,12 +2996,13 @@ In addition to the methods defined in the `Enumerable` contract, the `LazyCollec While the `each` method calls the given callback for each item in the collection right away, the `tapEach` method only calls the given callback as the items are being pulled out of the list one by one: + // Nothing has been dumped so far... $lazyCollection = LazyCollection::times(INF)->tapEach(function ($value) { dump($value); }); - // Nothing has been dumped so far... + // Three items are dumped... $array = $lazyCollection->take(3)->all(); // 1 From d220ea46f109b2387ad70982a3f1a99267b05bbf Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 11:52:15 -0600 Subject: [PATCH 105/274] remove space --- collections.md | 1 - 1 file changed, 1 deletion(-) diff --git a/collections.md b/collections.md index 750df31b44..94fd2dc678 100644 --- a/collections.md +++ b/collections.md @@ -3001,7 +3001,6 @@ While the `each` method calls the given callback for each item in the collection dump($value); }); - // Three items are dumped... $array = $lazyCollection->take(3)->all(); From 7532ccfaf87a86f54bdc57f5ede7f7bb1a2f7396 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 14:13:55 -0600 Subject: [PATCH 106/274] tailwind --- mix.md | 160 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 80 insertions(+), 80 deletions(-) diff --git a/mix.md b/mix.md index 0e87f415ae..2749e56a47 100644 --- a/mix.md +++ b/mix.md @@ -4,11 +4,9 @@ - [Installation & Setup](#installation) - [Running Mix](#running-mix) - [Working With Stylesheets](#working-with-stylesheets) - - [Less](#less) - - [Sass](#sass) - - [Stylus](#stylus) + - [Tailwind](#tailwind) - [PostCSS](#postcss) - - [Plain CSS](#plain-css) + - [Sass](#sass) - [URL Processing](#url-processing) - [Source Maps](#css-source-maps) - [Working With JavaScript](#working-with-scripts) @@ -25,12 +23,14 @@ <a name="introduction"></a> ## Introduction -[Laravel Mix](https://github.com/JeffreyWay/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: +[Laravel Mix](https://github.com/JeffreyWay/laravel-mix), a package developed by [Laracasts](https://laracasts.com) creator Jeffrey Way, provides a fluent API for defining [webpack](https://webpack.js.org) build steps for your Laravel application using several common CSS and JavaScript pre-processors. + +In other words, Mix makes it a cinch to compile and minify your application's CSS and JavaScript files. Through simple method chaining, you can fluently define your asset pipeline. For example: mix.js('resources/js/app.js', 'public/js') - .sass('resources/sass/app.scss', 'public/css'); + .postCss('resources/css/app.css', '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; you are free to use any asset pipeline tool you wish, or even none at all. +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; you are free to use any asset pipeline tool you wish, or even none at all. <a name="installation"></a> ## Installation & Setup @@ -38,24 +38,27 @@ If you've ever been confused and overwhelmed about getting started with Webpack <a name="installing-node"></a> #### Installing Node -Before triggering Mix, you must first ensure that Node.js and NPM are installed on your machine. +Before running Mix, you must first ensure that Node.js and NPM are installed on your machine: node -v npm -v -You can easily install the latest version of Node and NPM using simple graphical installers downloaded from [the official Node website](https://nodejs.org/en/download/). +You can easily install the latest version of Node and NPM using simple graphical installers from [the official Node website](https://nodejs.org/en/download/). Or, if you are using [Laravel Sail](/docs/{{version}}/installation#laravel-sail), you may invoke Node and NPM through Sail: + + ./sail node -v + ./sail npm -v -<a name="laravel-mix"></a> -#### Laravel Mix +<a name="installing-laravel-mix"></a> +#### Installing 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: +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 already includes everything you need to get started using Laravel Mix. Think of this file like your `composer.json` file, except it defines Node dependencies instead of PHP dependencies. You may install the dependencies it references by running: npm install <a name="running-mix"></a> ## 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: +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 in the default Laravel `package.json` file. When you run the `dev` or `production` scripts, all of your application's CSS and JavaScript assets will be compiled and placed in your application's `public` directory: // Run all Mix tasks... npm run dev @@ -66,100 +69,98 @@ Mix is a configuration layer on top of [Webpack](https://webpack.js.org), so to <a name="watching-assets-for-changes"></a> #### 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: +The `npm run watch` command will continue running in your terminal and watch all relevant CSS and JavaScript files for changes. Webpack will automatically recompile your assets when it detects a change to one of these files: npm run watch -You may find that in certain environments Webpack isn't updating when your files change. If this is the case on your system, consider using the `watch-poll` command: +Webpack may not be able to detect your file changes in certain local development environments. If this is the case on your system, consider using the `watch-poll` command: npm run watch-poll <a name="working-with-stylesheets"></a> ## 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. - -<a name="less"></a> -### 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/less/app.less', 'public/css'); +Your application's `webpack.mix.js` file is your entry point for all asset compilation. Think of it as a light configuration wrapper around [webpack](https://webpack.js.org). Mix tasks can be chained together to define exactly how your assets should be compiled. -Multiple calls to the `less` method may be used to compile multiple files: +<a name="tailwind"></a> +### Tailwind - mix.less('resources/less/app.less', 'public/css') - .less('resources/less/admin.less', 'public/css'); +[Tailwind CSS](https://tailwindcss.com) is a modern, utility-first framework for building amazing sites without ever leaving your HTML. Let's dig into how to start using it in a Laravel project with Laravel Mix. First, we should install Tailwind using NPM and generate our Tailwind configuration file: -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: + npm install tailwindcss@compat - mix.less('resources/less/app.less', 'public/stylesheets/styles.css'); + npx tailwindcss init -If you need to override the [underlying Less plug-in options](https://github.com/webpack-contrib/less-loader#options), you may pass an object as the third argument to `mix.less()`: +The `init` command will generate a `tailwind.config.js` file. Within this file, you may configure the paths to all of your application's templates and JavaScript so that Tailwind can tree-shake unused styles when optimizing your CSS for production: - mix.less('resources/less/app.less', 'public/css', { - strictMath: true - }); +```js +purge: [ + './storage/framework/views/*.php', + './resources/**/*.blade.php', + './resources/**/*.js', + './resources/**/*.vue', +], +``` -<a name="sass"></a> -### Sass +Next, you should add each of Tailwind's "layers" to your application's `resources/css/app.css` file: -The `sass` method allows you to compile [Sass](https://sass-lang.com/) into CSS. You may use the method like so: +```css +@tailwind base; +@tailwind components; +@tailwind utilities; +``` - mix.sass('resources/sass/app.scss', 'public/css'); +Once you have configured Tailwind's layers, you are ready to update your application's `webpack.mix.js` file to compile your Tailwind powered 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: +```js +mix.js('resources/js/app.js', 'public/js') + .postCss('resources/css/app.css', 'public/css', [ + require('postcss-import'), + require('tailwindcss'), + ]); +``` - mix.sass('resources/sass/app.sass', 'public/css') - .sass('resources/sass/admin.sass', 'public/css/admin'); +Finally, you should reference your stylesheet in your application's primary layout template. Many applications choose to store this template at `resources/views/layouts/app.blade.php`. In addition, ensure you add the responsive viewport `meta` tag if it's not already present: -Additional [Node-Sass plug-in options](https://github.com/sass/node-sass#options) may be provided as the third argument: +```html +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <link href="{{ mix('css/app.css') }}" rel="stylesheet"> +</head> +``` - mix.sass('resources/sass/app.sass', 'public/css', { - precision: 5 - }); +<a name="postcss"></a> +### PostCSS -<a name="stylus"></a> -### Stylus +[PostCSS](https://postcss.org/), a powerful tool for transforming your CSS, is included with Laravel Mix out of the box. By default, Mix leverages the popular [Autoprefixer](https://github.com/postcss/autoprefixer) plugin to automatically apply all necessary CSS3 vendor prefixes. However, you're free to add any additional plugins that are appropriate for your application. -Similar to Less and Sass, the `stylus` method allows you to compile [Stylus](http://stylus-lang.com/) into CSS: +First, install the desired plugin through NPM and include it in your array of plugins when calling Mix's `postCss` method. The `postCss` method accepts the path to your CSS file as its first argument and the directory where the compiled file should be placed as its second argument: - mix.stylus('resources/stylus/app.styl', 'public/css'); + mix.postCss('resources/css/app.css', 'public/css', [ + require('postcss-custom-properties') + ]); -You may also install additional Stylus plug-ins, such as [Rupture](https://github.com/jescalan/rupture). First, install the plug-in in question through NPM (`npm install rupture`) and then require it in your call to `mix.stylus()`: +Or, you may execute `postCss` with no additional plugins in order to achieve simple CSS compilation and minification: - mix.stylus('resources/stylus/app.styl', 'public/css', { - use: [ - require('rupture')() - ] - }); + mix.postCss('resources/css/app.css', 'public/css'); -<a name="postcss"></a> -### PostCSS - -[PostCSS](https://postcss.org/), a powerful tool for transforming your CSS, is included with Laravel Mix out of the box. By default, Mix leverages the popular [Autoprefixer](https://github.com/postcss/autoprefixer) plug-in to automatically apply all necessary CSS3 vendor prefixes. However, you're free to add any additional plug-ins that are appropriate for your application. First, install the desired plug-in through NPM and then reference it in your `webpack.mix.js` file: +<a name="sass"></a> +### Sass - mix.sass('resources/sass/app.scss', 'public/css') - .options({ - postCss: [ - require('postcss-css-variables')() - ] - }); +The `sass` method allows you to compile [Sass](https://sass-lang.com/) into CSS that can be understood by web browsers. The `sass` method accepts the path to your Sass file as its first argument and the directory where the compiled file should be placed as its second argument: -<a name="plain-css"></a> -### Plain CSS + mix.sass('resources/sass/app.scss', 'public/css'); -If you would just like to concatenate some plain CSS stylesheets into a single file, you may use the `styles` method. +Y may compile multiple Sass files into their own respective CSS files and even customize the output directory of the resulting CSS by calling the `sass` method multiple times: - mix.styles([ - 'public/css/vendor/normalize.css', - 'public/css/vendor/videojs.css' - ], 'public/css/all.css'); + mix.sass('resources/sass/app.sass', 'public/css') + .sass('resources/sass/admin.sass', 'public/css/admin'); <a name="url-processing"></a> ### URL Processing -Because Laravel Mix is built on top of Webpack, it's important to understand a few Webpack concepts. For CSS compilation, Webpack will rewrite and optimize any `url()` calls within your stylesheets. While this might initially sound strange, it's an incredibly powerful piece of functionality. Imagine that we want to compile Sass that includes a relative URL to an image: +Because Laravel Mix is built on top of webpack, it's important to understand a few webpack concepts. For CSS compilation, webpack will rewrite and optimize any `url()` calls within your stylesheets. While this might initially sound strange, it's an incredibly powerful piece of functionality. Imagine that we want to compile Sass that includes a relative URL to an image: .example { background: url('../images/example.png'); @@ -167,7 +168,7 @@ Because Laravel Mix is built on top of Webpack, it's important to understand a f > {note} Absolute paths for any given `url()` will be excluded from URL-rewriting. For example, `url('/images/thing.png')` or `url('http://example.com/images/thing.png')` won't be modified. -By default, Laravel Mix and Webpack will find `example.png`, copy it to your `public/images` folder, and then rewrite the `url()` within your generated stylesheet. As such, your compiled CSS will be: +By default, Laravel Mix and webpack will find `example.png`, copy it to your `public/images` folder, and then rewrite the `url()` within your generated stylesheet. As such, your compiled CSS will be: .example { background: url(/images/example.png?d41d8cd98f00b204e9800998ecf8427e); @@ -175,10 +176,9 @@ By default, Laravel Mix and Webpack will find `example.png`, copy it to your `pu As useful as this feature may be, it's possible that your existing folder structure is already configured in a way you like. If this is the case, you may disable `url()` rewriting like so: - mix.sass('resources/sass/app.scss', 'public/css') - .options({ - processCssUrls: false - }); + mix.sass('resources/sass/app.scss', 'public/css').options({ + processCssUrls: false + }); With this addition to your `webpack.mix.js` file, Mix will no longer match any `url()` or copy assets to your public directory. In other words, the compiled CSS will look just like how you originally typed it: @@ -189,7 +189,7 @@ With this addition to your `webpack.mix.js` file, Mix will no longer match any ` <a name="css-source-maps"></a> ### 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. +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/js/app.js', 'public/js') .sourceMaps(); @@ -247,11 +247,11 @@ To avoid JavaScript errors, be sure to load these files in the proper order: <a name="react"></a> ### React -Mix can automatically install the Babel plug-ins necessary for React support. To get started, replace your `mix.js()` call with `mix.react()`: +Mix can automatically install the Babel plugins necessary for React support. To get started, replace your `mix.js()` call with `mix.react()`: mix.react('resources/js/app.jsx', 'public/js'); -Behind the scenes, Mix will download and include the appropriate `babel-preset-react` Babel plug-in. +Behind the scenes, Mix will download and include the appropriate `babel-preset-react` Babel plugin. <a name="vanilla-js"></a> ### Vanilla JS @@ -270,7 +270,7 @@ This option is particularly useful for legacy projects where you don't require W <a name="custom-webpack-configuration"></a> ### 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 plug-in that needs to be referenced, or maybe you prefer to use Stylus instead of Sass. In such instances, you have two choices: +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: <a name="merging-custom-configuration"></a> #### Merging Custom Configuration From 57ca0995e5b26299d59f4c2dae31d7971274f551 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 14:36:41 -0600 Subject: [PATCH 107/274] wip --- mix.md | 123 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/mix.md b/mix.md index 2749e56a47..b6c8c2ce4b 100644 --- a/mix.md +++ b/mix.md @@ -4,17 +4,16 @@ - [Installation & Setup](#installation) - [Running Mix](#running-mix) - [Working With Stylesheets](#working-with-stylesheets) - - [Tailwind](#tailwind) + - [Tailwind CSS](#tailwindcss) - [PostCSS](#postcss) - [Sass](#sass) - [URL Processing](#url-processing) - [Source Maps](#css-source-maps) - [Working With JavaScript](#working-with-scripts) - - [Vendor Extraction](#vendor-extraction) + - [Vue](#vue) - [React](#react) - - [Vanilla JS](#vanilla-js) + - [Vendor Extraction](#vendor-extraction) - [Custom Webpack Configuration](#custom-webpack-configuration) -- [Copying Files & Directories](#copying-files-and-directories) - [Versioning / Cache Busting](#versioning-and-cache-busting) - [Browsersync Reloading](#browsersync-reloading) - [Environment Variables](#environment-variables) @@ -82,8 +81,8 @@ Webpack may not be able to detect your file changes in certain local development Your application's `webpack.mix.js` file is your entry point for all asset compilation. Think of it as a light configuration wrapper around [webpack](https://webpack.js.org). Mix tasks can be chained together to define exactly how your assets should be compiled. -<a name="tailwind"></a> -### Tailwind +<a name="tailwindcss"></a> +### Tailwind CSS [Tailwind CSS](https://tailwindcss.com) is a modern, utility-first framework for building amazing sites without ever leaving your HTML. Let's dig into how to start using it in a Laravel project with Laravel Mix. First, we should install Tailwind using NPM and generate our Tailwind configuration file: @@ -126,7 +125,7 @@ Finally, you should reference your stylesheet in your application's primary layo <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <link href="{{ mix('css/app.css') }}" rel="stylesheet"> + <link href="/css/app.css" rel="stylesheet"> </head> ``` @@ -216,21 +215,54 @@ With this single line of code, you may now take advantage of: <div class="content-list" markdown="1"> - ES2015 syntax. - Modules -- Compilation of `.vue` files. - Minification for production environments. </div> +<a name="vue"></a> +### Vue + +Mix will automatically install the Babel plugins necessary for Vue single-file component compilation support when using the `js` method. No further configuration is required: + + mix.js('resources/js/app.js', 'public/js'); + +Once your JavaScript has been compiled, you can reference it in your application: + +```html +<head> + <!-- ... --> + + <script src="/js/app.js"></script> +</head> +``` + +<a name="react"></a> +### React + +Mix can automatically install the Babel plugins necessary for React support. To get started, replace your call to Mix's `js` method with a call to the `react` method: + + mix.react('resources/js/app.jsx', 'public/js'); + +Behind the scenes, Mix will download and include the appropriate `babel-preset-react` Babel plugin. Once your JavaScript has been compiled, you can reference it in your application: + +```html +<head> + <!-- ... --> + + <script src="/js/app.js"></script> +</head> +``` + <a name="vendor-extraction"></a> ### Vendor Extraction -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. +One potential downside to bundling all of your application-specific JavaScript with your vendor libraries such as React and Vue 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 own 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/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: +The `extract` method accepts an array of all libraries or modules that you wish to extract into a `vendor.js` file. Using the snippet above as an example, Mix will generate the following files: <div class="content-list" markdown="1"> - `public/js/manifest.js`: *The Webpack manifest runtime* @@ -244,33 +276,10 @@ To avoid JavaScript errors, be sure to load these files in the proper order: <script src="/js/vendor.js"></script> <script src="/js/app.js"></script> -<a name="react"></a> -### React - -Mix can automatically install the Babel plugins necessary for React support. To get started, replace your `mix.js()` call with `mix.react()`: - - mix.react('resources/js/app.jsx', 'public/js'); - -Behind the scenes, Mix will download and include the appropriate `babel-preset-react` Babel plugin. - -<a name="vanilla-js"></a> -### Vanilla JS - -Similar to combining stylesheets with `mix.styles()`, you may also combine and minify any number of JavaScript files with the `scripts()` method: - - mix.scripts([ - 'public/js/admin.js', - 'public/js/dashboard.js' - ], 'public/js/all.js'); - -This option is particularly useful for legacy projects where you don't require Webpack compilation for your JavaScript. - -> {tip} A slight variation of `mix.scripts()` is `mix.babel()`. Its method signature is identical to `scripts`; however, the concatenated file will receive Babel compilation, which translates any ES2015 code to vanilla JavaScript that all browsers will understand. - <a name="custom-webpack-configuration"></a> ### 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: +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. For example, you might have a special loader or plugin that needs to be referenced. In such instances, you have two choices: <a name="merging-custom-configuration"></a> #### Merging Custom Configuration @@ -290,23 +299,12 @@ Mix provides a useful `webpackConfig` method that allows you to merge any short If you would like to completely customize your Webpack configuration, copy the `node_modules/laravel-mix/setup/webpack.config.js` file to your project's root directory. Next, point all of the `--config` references in your `package.json` file to the newly copied configuration file. If you choose to take this approach to customization, any future upstream updates to Mix's `webpack.config.js` must be manually merged into your customized file. -<a name="copying-files-and-directories"></a> -## 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'); - -When copying a directory, the `copy` method will flatten the directory's structure. To maintain the directory's original structure, you should use the `copyDirectory` method instead: - - mix.copyDirectory('resources/img', 'public/img'); - <a name="versioning-and-cache-busting"></a> ## 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. +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 automatically handle this for you using the `version` method. -The `version` method will automatically append a unique hash to the filenames of all compiled files, allowing for more convenient cache busting: +The `version` method will append a unique hash to the filenames of all compiled files, allowing for more convenient cache busting: mix.js('resources/js/app.js', 'public/js') .version(); @@ -326,44 +324,49 @@ Because versioned files are usually unnecessary in development, you may instruct <a name="custom-mix-base-urls"></a> #### Custom Mix Base URLs -If your Mix compiled assets are deployed to a CDN separate from your application, you will need to change the base URL generated by the `mix` function. You may do so by adding a `mix_url` configuration option to your `config/app.php` configuration file: +If your Mix compiled assets are deployed to a CDN separate from your application, you will need to change the base URL generated by the `mix` function. You may do so by adding a `mix_url` configuration option to your application's `config/app.php` configuration file: 'mix_url' => env('MIX_ASSET_URL', null) After configuring the Mix URL, The `mix` function will prefix the configured URL when generating URLs to assets: - https://cdn.example.com/js/app.js?id=1964becbdd96414518cd +```bash +https://cdn.example.com/js/app.js?id=1964becbdd96414518cd +``` <a name="browsersync-reloading"></a> ## Browsersync Reloading -[BrowserSync](https://browsersync.io/) can automatically monitor your files for changes, and inject your changes into the browser without requiring a manual refresh. You may enable support by calling the `mix.browserSync()` method: +[BrowserSync](https://browsersync.io/) can automatically monitor your files for changes, and inject your changes into the browser without requiring a manual refresh. You may enable support for this by calling the `mix.browserSync()` method: - mix.browserSync('my-domain.test'); +```js +mix.browserSync('laravel.test'); +``` - // Or... +[BrowserSync options](https://browsersync.io/docs/options) may be specified by passing a JavaScript object to the `browserSync` method: - // https://browsersync.io/docs/options - mix.browserSync({ - proxy: 'my-domain.test' - }); +```js +mix.browserSync({ + proxy: 'laravel.test' +}); +``` -You may pass either a string (proxy) or object (BrowserSync settings) to this method. Next, start Webpack's dev server using the `npm run watch` command. Now, when you modify a script or PHP file, watch as the browser instantly refreshes the page to reflect your changes. +Next, start webpack's development server using the `npm run watch` command. Now, when you modify a script or PHP file you can watch as the browser instantly refreshes the page to reflect your changes. <a name="environment-variables"></a> ## Environment Variables -You may inject environment variables into Mix by prefixing a key in your `.env` file with `MIX_`: +You may inject environment variables into your `webpack.mix.js` script by prefixing one of the environment variables in your `.env` file with `MIX_`: MIX_SENTRY_DSN_PUBLIC=http://example.com -After the variable has been defined in your `.env` file, you may access it via the `process.env` object. If the value changes while you are running a `watch` task, you will need to restart the task: +After the variable has been defined in your `.env` file, you may access it via the `process.env` object. However, you will need to restart the task if the environment variable's value changes while the task is running: process.env.MIX_SENTRY_DSN_PUBLIC <a name="notifications"></a> ## 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. +When available, Mix will automatically display OS notifications when compiling, giving you instant feedback as to whether the compilation was successful or not. However, there may be instances when you would prefer to disable these notifications. One such example might be triggering Mix on your production server. Notifications may be deactivated using the `disableNotifications` method: mix.disableNotifications(); From f8800e7a71c959d08085cf70e39a8d6a4b6e0b8d Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 14:39:49 -0600 Subject: [PATCH 108/274] wip --- starter-kits.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/starter-kits.md b/starter-kits.md index 5a88ea26c9..4ebf6ac019 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -24,16 +24,22 @@ php artisan migrate Once you have created a new Laravel application, you may install Laravel Breeze using Composer: - composer require laravel/breeze --dev +```bash +composer require laravel/breeze --dev +``` After Composer has installed the Laravel Breeze package, you may run the `breeze:install` Artisan command. This command publishes the authentication views, routes, controllers, and other resources to your application. Laravel Breeze publishes all of its code to your application so that you have full control and visibility over its features and implementation. After Breeze is installed, you should also compile your assets so that your application's CSS file is available: - php artisan breeze:install - - npm install && npm run dev +```bash +php artisan breeze:install +npm install +npm run dev +``` Next, you may navigate to your application's `/login` or `/register` URLs in your web browser. All of Breeze's routes are defined within the `routes/auth.php` file. +> {tip} To learn more about compiling your application's CSS and JavaScript, check out the [Laravel Mix documentation](/docs/{{version}}/mix#running-mix). + <a name="laravel-jetstream"></a> ## Laravel Jetstream From 4a45b9a61003914112fe72a71d6b84b35da3254d Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 14:44:09 -0600 Subject: [PATCH 109/274] wip --- mix.md | 2 ++ starter-kits.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/mix.md b/mix.md index b6c8c2ce4b..78cd4ed50c 100644 --- a/mix.md +++ b/mix.md @@ -31,6 +31,8 @@ In other words, Mix makes it a cinch to compile and minify your application's CS 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; you are free to use any asset pipeline tool you wish, or even none at all. +> {tip} If you need a head start building your application with Laravel and [Tailwind CSS](https://tailwindcss.com), check out one of our [application starter kits](/docs/{{version}}/starter-kits). + <a name="installation"></a> ## Installation & Setup diff --git a/starter-kits.md b/starter-kits.md index 4ebf6ac019..ef0a4c3fe7 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -32,7 +32,9 @@ After Composer has installed the Laravel Breeze package, you may run the `breeze ```bash php artisan breeze:install + npm install + npm run dev ``` From e0c5ad9fc11870e6e10c89cd793d6eaf684128d4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 15:08:02 -0600 Subject: [PATCH 110/274] wip --- events.md | 131 +++++++++++++++++++++++++++--------------------------- 1 file changed, 65 insertions(+), 66 deletions(-) diff --git a/events.md b/events.md index 4933bbbfc5..ec964f3b8f 100644 --- a/events.md +++ b/events.md @@ -8,7 +8,7 @@ - [Defining Events](#defining-events) - [Defining Listeners](#defining-listeners) - [Queued Event Listeners](#queued-event-listeners) - - [Manually Accessing The Queue](#manually-accessing-the-queue) + - [Manually Interacting The Queue](#manually-interacting-the-queue) - [Handling Failed Jobs](#handling-failed-jobs) - [Dispatching Events](#dispatching-events) - [Event Subscribers](#event-subscribers) @@ -18,14 +18,17 @@ <a name="introduction"></a> ## Introduction -Laravel's events provide a simple observer implementation, allowing you to subscribe and listen for various events that occur in your application. Event classes are typically stored in the `app/Events` directory, while their listeners are stored in `app/Listeners`. Don't worry if you don't see these directories in your application, since they will be created for you as you generate events and listeners using Artisan console commands. +Laravel's events provide a simple observer pattern implementation, allowing you to subscribe and listen for various events that occur within your application. Event classes are typically stored in the `app/Events` directory, while their listeners are stored in `app/Listeners`. Don't worry if you don't see these directories in your application as they will be created for you as you generate events and listeners using Artisan console commands. -Events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other. For example, you may wish to send a Slack notification to your user each time an order has shipped. Instead of coupling your order processing code to your Slack notification code, you can raise an `OrderShipped` event, which a listener can receive and transform into a Slack notification. +Events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other. For example, you may wish to send a Slack notification to your user each time an order has shipped. Instead of coupling your order processing code to your Slack notification code, you can raise an `App\Events\OrderShipped` event which a listener can receive and use to dispatch a Slack notification. <a name="registering-events-and-listeners"></a> ## Registering Events & Listeners -The `EventServiceProvider` included with your Laravel application provides a convenient place to register all of your application's event listeners. The `listen` property contains an array of all events (keys) and their listeners (values). You may add as many events to this array as your application requires. For example, let's add an `OrderShipped` event: +The `App\Providers\EventServiceProvider` included with your Laravel application provides a convenient place to register all of your application's event listeners. The `listen` property contains an array of all events (keys) and their listeners (values). You may add as many events to this array as your application requires. For example, let's add an `OrderShipped` event: + + use App\Events\OrderShipped; + use App\Listeners\SendShipmentNotification; /** * The event listener mappings for the application. @@ -33,24 +36,34 @@ The `EventServiceProvider` included with your Laravel application provides a con * @var array */ protected $listen = [ - 'App\Events\OrderShipped' => [ - 'App\Listeners\SendShipmentNotification', + OrderShipped::class => [ + SendShipmentNotification::class, ], ]; +> {tip} The `event:list` command may be used to display a list of all events and listeners registered by your application. + <a name="generating-events-and-listeners"></a> ### Generating Events & Listeners -Of course, manually creating the files for each event and listener is cumbersome. Instead, add listeners and events to your `EventServiceProvider` and use the `event:generate` command. This command will generate any events or listeners that are listed in your `EventServiceProvider`. Events and listeners that already exist will be left untouched: +Of course, manually creating the files for each event and listener is cumbersome. Instead, add listeners and events to your `EventServiceProvider` and use the `event:generate` Artisan command. This command will generate any events or listeners that are listed in your `EventServiceProvider` that do not already exist: php artisan event:generate +Alternatively, you may use the `make:event` and `make:listener` Artisan commands to generate individual events and listeners: + + php artisan make:event PodcastProcessed + + php artisan make:listener SendPodcastNotification --event=PodcastProcessed + <a name="manually-registering-events"></a> ### 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`: +Typically, events should be registered via the `EventServiceProvider` `$listen` array; however, you may also register class or closure based event listeners manually in the `boot` method of your `EventServiceProvider`: use App\Events\PodcastProcessed; + use App\Listeners\SendPodcastNotification; + use Illuminate\Support\Facades\Event; /** * Register any other events for your application. @@ -59,6 +72,11 @@ Typically, events should be registered via the `EventServiceProvider` `$listen` */ public function boot() { + Event::listen( + PodcastProcessed::class, + [SendPodcastNotification::class, 'handle'] + ); + Event::listen(function (PodcastProcessed $event) { // }); @@ -67,7 +85,7 @@ Typically, events should be registered via the `EventServiceProvider` `$listen` <a name="queuable-anonymous-event-listeners"></a> #### Queueable Anonymous Event Listeners -When registering event listeners manually, you may wrap the listener closure within the `Illuminate\Events\queueable` function to instruct Laravel to execute the listener using the [queue](/docs/{{version}}/queues): +When registering closure based event listeners manually, you may wrap the listener closure within the `Illuminate\Events\queueable` function to instruct Laravel to execute the listener using the [queue](/docs/{{version}}/queues): use App\Events\PodcastProcessed; use function Illuminate\Events\queueable; @@ -91,7 +109,7 @@ Like queued jobs, you may use the `onConnection`, `onQueue`, and `delay` methods // })->onConnection('redis')->onQueue('podcasts')->delay(now()->addSeconds(10))); -If you would like to handle anonymous queued listener failures, you may provide a closure to the `catch` method while defining the `queueable` listener: +If you would like to handle anonymous queued listener failures, you may provide a closure to the `catch` method while defining the `queueable` listener. This closure will receive the event instance and the `Throwable` instance that caused the listener's failure: use App\Events\PodcastProcessed; use function Illuminate\Events\queueable; @@ -107,7 +125,7 @@ If you would like to handle anonymous queued listener failures, you may provide <a name="wildcard-event-listeners"></a> #### 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: +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) { // @@ -118,11 +136,11 @@ You may even register listeners using the `*` as a wildcard parameter, allowing Instead of registering events and listeners manually in the `$listen` array of the `EventServiceProvider`, you can enable automatic event discovery. When event discovery is enabled, Laravel will automatically find and register your events and listeners by scanning your application's `Listeners` directory. In addition, any explicitly defined events listed in the `EventServiceProvider` will still be registered. -Laravel finds event listeners by scanning the listener classes using reflection. When Laravel finds any listener class method that begins with `handle`, Laravel will register those methods as event listeners for the event that is type-hinted in the method's signature: +Laravel finds event listeners by scanning the listener classes using PHP's reflection services. When Laravel finds any listener class method that begins with `handle`, Laravel will register those methods as event listeners for the event that is type-hinted in the method's signature: use App\Events\PodcastProcessed; - class SendPodcastProcessedNotification + class SendPodcastNotification { /** * Handle the given event. @@ -148,7 +166,7 @@ Event discovery is disabled by default, but you can enable it by overriding the return true; } -By default, all listeners within your application's Listeners directory will be scanned. If you would like to define additional directories to scan, you may override the `discoverEventsWithin` method in your `EventServiceProvider`: +By default, all listeners within your application's `app/Listeners` directory will be scanned. If you would like to define additional directories to scan, you may override the `discoverEventsWithin` method in your `EventServiceProvider`: /** * Get the listener directories that should be used to discover events. @@ -162,14 +180,15 @@ By default, all listeners within your application's Listeners directory will be ]; } -In production, you likely do not want the framework to scan all of your listeners on every request. Therefore, during your deployment process, you should run the `event:cache` Artisan command to cache a manifest of all of your application's events and listeners. This manifest will be used by the framework to speed up the event registration process. The `event:clear` command may be used to destroy the cache. +<a name="event-discovery-in-production"></a> +#### Event Discovery In Production -> {tip} The `event:list` command may be used to display a list of all events and listeners registered by your application. +In production, it is not efficient for the framework to scan all of your listeners on every request. Therefore, during your deployment process, you should run the `event:cache` Artisan command to cache a manifest of all of your application's events and listeners. This manifest will be used by the framework to speed up the event registration process. The `event:clear` command may be used to destroy the cache. <a name="defining-events"></a> ## Defining Events -An event class is a data container which holds the information related to the event. For example, let's assume our generated `OrderShipped` event receives an [Eloquent ORM](/docs/{{version}}/eloquent) object: +An event class is essentially a data container which holds the information related to the event. For example, let's assume an `App\Events\OrderShipped` event receives an [Eloquent ORM](/docs/{{version}}/eloquent) object: <?php @@ -184,6 +203,11 @@ An event class is a data container which holds the information related to the ev { use Dispatchable, InteractsWithSockets, SerializesModels; + /** + * The order instance. + * + * @var \App\Models\Order + */ public $order; /** @@ -198,12 +222,12 @@ An event class is a data container which holds the information related to the ev } } -As you can see, this event class contains no logic. It is a container for the `Order` instance that was purchased. The `SerializesModels` trait used by the event will gracefully serialize any Eloquent models if the event object is serialized using PHP's `serialize` function. +As you can see, this event class contains no logic. It is a container for the `App\Models\Order` instance that was purchased. The `SerializesModels` trait used by the event will gracefully serialize any Eloquent models if the event object is serialized using PHP's `serialize` function, such as when utilizing [queued listeners](#queued-event-listeners). <a name="defining-listeners"></a> ## Defining Listeners -Next, let's take a look at the listener for our example event. Event listeners receive the event instance in their `handle` method. The `event:generate` command will automatically import the proper event class and type-hint the event on the `handle` method. Within the `handle` method, you may perform any actions necessary to respond to the event: +Next, let's take a look at the listener for our example event. Event listeners receive event instances in their `handle` method. The `event:generate` and `make:listener` Artisan commands will automatically import the proper event class and type-hint the event on the `handle` method. Within the `handle` method, you may perform any actions necessary to respond to the event: <?php @@ -245,9 +269,9 @@ Sometimes, you may wish to stop the propagation of an event to other listeners. <a name="queued-event-listeners"></a> ## Queued Event Listeners -Queueing listeners can be beneficial if your listener is going to perform a slow task such as sending an e-mail or making an HTTP request. Before getting started with queued listeners, make sure to [configure your queue](/docs/{{version}}/queues) and start a queue listener on your server or local development environment. +Queueing listeners can be beneficial if your listener is going to perform a slow task such as sending an email or making an HTTP request. Before using queued listeners, make sure to [configure your queue](/docs/{{version}}/queues) and start a queue worker on your server or local development environment. -To specify that a listener should be queued, add the `ShouldQueue` interface to the listener class. Listeners generated by the `event:generate` Artisan command already have this interface imported into the current namespace, so you can use it immediately: +To specify that a listener should be queued, add the `ShouldQueue` interface to the listener class. Listeners generated by the `event:generate` and `make:listener` Artisan commands already have this interface imported into the current namespace so you can use it immediately: <?php @@ -261,7 +285,7 @@ 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. +That's it! Now, when an event handled by this listener is dispatched, the listener will automatically be 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. <a name="customizing-the-queue-connection-queue-name"></a> #### Customizing The Queue Connection & Queue Name @@ -320,7 +344,7 @@ Sometimes, you may need to determine whether a listener should be queued based o namespace App\Listeners; - use App\Events\OrderPlaced; + use App\Events\OrderCreated; use Illuminate\Contracts\Queue\ShouldQueue; class RewardGiftCard implements ShouldQueue @@ -328,10 +352,10 @@ Sometimes, you may need to determine whether a listener should be queued based o /** * Reward a gift card to the customer. * - * @param \App\Events\OrderPlaced $event + * @param \App\Events\OrderCreated $event * @return void */ - public function handle(OrderPlaced $event) + public function handle(OrderCreated $event) { // } @@ -339,17 +363,17 @@ Sometimes, you may need to determine whether a listener should be queued based o /** * Determine whether the listener should be queued. * - * @param \App\Events\OrderPlaced $event + * @param \App\Events\OrderCreated $event * @return bool */ - public function shouldQueue(OrderPlaced $event) + public function shouldQueue(OrderCreated $event) { return $event->order->subtotal >= 5000; } } -<a name="manually-accessing-the-queue"></a> -### Manually Accessing The Queue +<a name="manually-interacting-the-queue"></a> +### Manually Interacting The Queue If you need to manually access the listener's underlying queue job's `delete` and `release` methods, you may do so using the `Illuminate\Queue\InteractsWithQueue` trait. This trait is imported by default on generated listeners and provides access to these methods: @@ -382,7 +406,7 @@ If you need to manually access the listener's underlying queue job's `delete` an <a name="handling-failed-jobs"></a> ### 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: +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 `Throwable` that caused the failure: <?php @@ -423,7 +447,7 @@ Sometimes your queued event listeners may fail. If queued listener exceeds the m <a name="dispatching-events"></a> ## Dispatching Events -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: +To dispatch an event, you may call the static `dispatch` method on the event. This method is made available on the event by the `Illuminate\Foundation\Events\Dispatchable` trait. Any arguments passed to the `dispatch` method will be passed to the event's constructor: <?php @@ -432,29 +456,26 @@ To dispatch an event, you may pass an instance of the event to the `event` helpe use App\Events\OrderShipped; use App\Http\Controllers\Controller; use App\Models\Order; + use Illuminate\Http\Request; - class OrderController extends Controller + class OrderShipmentController extends Controller { /** * Ship the given order. * - * @param int $orderId - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ - public function ship($orderId) + public function store(Request $request) { - $order = Order::findOrFail($orderId); + $order = Order::findOrFail($request->order_id); // Order shipment logic... - event(new OrderShipped($order)); + OrderShipped::dispatch($order); } } -Alternatively, if your event uses the `Illuminate\Foundation\Events\Dispatchable` trait, you may call the static `dispatch` method on the event. Any arguments passed to the `dispatch` method will be passed to the event's constructor: - - OrderShipped::dispatch($order); - > {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#event-fake) makes it a cinch. <a name="event-subscribers"></a> @@ -463,7 +484,7 @@ Alternatively, if your event uses the `Illuminate\Foundation\Events\Dispatchable <a name="writing-event-subscribers"></a> ### Writing Event Subscribers -Event subscribers are classes that may subscribe to multiple events from within the class itself, allowing you to define several event handlers within a single class. Subscribers should define a `subscribe` method, which will be passed an event dispatcher instance. You may call the `listen` method on the given dispatcher to register event listeners: +Event subscribers are classes that may subscribe to multiple events from within the subscriber class itself, allowing you to define several event handlers within a single class. Subscribers should define a `subscribe` method, which will be passed an event dispatcher instance. You may call the `listen` method on the given dispatcher to register event listeners: <?php @@ -501,29 +522,6 @@ Event subscribers are classes that may subscribe to multiple events from within } } -Alternatively, your subscriber's `subscribe` method may return an array of event to handler mappings. In this case, the event listener mappings will be registered for you automatically: - - use Illuminate\Auth\Events\Login; - use Illuminate\Auth\Events\Logout; - - /** - * Register the listeners for the subscriber. - * - * @return array - */ - public function subscribe() - { - return [ - Login::class => [ - [UserEventSubscriber::class, 'handleUserLogin'] - ], - - Logout::class => [ - [UserEventSubscriber::class, 'handleUserLogout'] - ], - ]; - } - <a name="registering-event-subscribers"></a> ### Registering Event Subscribers @@ -533,6 +531,7 @@ After writing the subscriber, you are ready to register it with the event dispat namespace App\Providers; + use App\Listeners\UserEventSubscriber; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class EventServiceProvider extends ServiceProvider @@ -552,6 +551,6 @@ After writing the subscriber, you are ready to register it with the event dispat * @var array */ protected $subscribe = [ - 'App\Listeners\UserEventSubscriber', + UserEventSubscriber::class, ]; } From 63a09f308236c35700c629ccd0e8c824ac84f33a Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 16:01:23 -0600 Subject: [PATCH 111/274] wip --- filesystem.md | 183 +++++++++++++++++++++++++------------------------- 1 file changed, 93 insertions(+), 90 deletions(-) diff --git a/filesystem.md b/filesystem.md index 85ae480935..3942801ab5 100644 --- a/filesystem.md +++ b/filesystem.md @@ -2,15 +2,14 @@ - [Introduction](#introduction) - [Configuration](#configuration) - - [The Public Disk](#the-public-disk) - [The Local Driver](#the-local-driver) + - [The Public Disk](#the-public-disk) - [Driver Prerequisites](#driver-prerequisites) - [Caching](#caching) - [Obtaining Disk Instances](#obtaining-disk-instances) - [Retrieving Files](#retrieving-files) - [Downloading Files](#downloading-files) - [File URLs](#file-urls) - - [File Paths](#file-paths) - [File Metadata](#file-metadata) - [Storing Files](#storing-files) - [File Uploads](#file-uploads) @@ -22,19 +21,32 @@ <a name="introduction"></a> ## Introduction -Laravel provides a powerful filesystem abstraction thanks to the wonderful [Flysystem](https://github.com/thephpleague/flysystem) PHP package by Frank de Jonge. The Laravel Flysystem integration provides simple to use drivers for working with local filesystems and Amazon S3. Even better, it's amazingly simple to switch between these storage options as the API remains the same for each system. +Laravel provides a powerful filesystem abstraction thanks to the wonderful [Flysystem](https://github.com/thephpleague/flysystem) PHP package by Frank de Jonge. The Laravel Flysystem integration provides simple drivers for working with local filesystems, SFTP, and Amazon S3. Even better, it's amazingly simple to switch between these storage options between your local development machine and production server as the API remains the same for each system. <a name="configuration"></a> ## Configuration -The filesystem configuration file is located at `config/filesystems.php`. Within this file you may configure all of your "disks". Each disk represents a particular storage driver and storage location. Example configurations for each supported driver are included in the configuration file. So, modify the configuration to reflect your storage preferences and credentials. +Laravel's filesystem configuration file is located at `config/filesystems.php`. Within this file you may configure all of your filesystem "disks". Each disk represents a particular storage driver and storage location. Example configurations for each supported driver are included in the configuration file so you can modify the configuration to reflect your storage preferences and credentials. + +The `local` driver interacts with files stored locally on the server running the Laravel application while the `s3` driver is used to write to Amazon's S3 cloud storage service. + +> {tip} You may configure as many disks as you like and may even have multiple disks that use the same driver. + +<a name="the-local-driver"></a> +### The Local Driver + +When using the `local` driver, all file operations are relative to the `root` directory defined in your `filesystems` configuration file. By default, this value is set to the `storage/app` directory. Therefore, the following method would write to `storage/app/example.txt`: + + use Illuminate\Support\Facades\Storage; -You may configure as many disks as you like, and may even have multiple disks that use the same driver. + Storage::disk('local')->put('example.txt', 'Contents'); <a name="the-public-disk"></a> ### The Public Disk -The `public` disk is intended for files that are going to be publicly accessible. By default, the `public` disk uses the `local` driver and stores these files in `storage/app/public`. To make them accessible from the web, you should create a symbolic link from `public/storage` to `storage/app/public`. This convention will keep your publicly accessible files in one directory that can be easily shared across deployments when using zero down-time deployment systems like [Envoyer](https://envoyer.io). +The `public` disk included in your application's `filesystems` configuration file is intended for files that are going to be publicly accessible. By default, the `public` disk uses the `local` driver and stores its files in `storage/app/public`. + +To make these files accessible from the web, you should create a symbolic link from `public/storage` to `storage/app/public`. Utilizing this folder convention will keep your publicly accessible files in one directory that can be easily shared across deployments when using zero down-time deployment systems like [Envoyer](https://envoyer.io). To create the symbolic link, you may use the `storage:link` Artisan command: @@ -51,45 +63,18 @@ You may configure additional symbolic links in your `filesystems` configuration public_path('images') => storage_path('app/images'), ], -<a name="the-local-driver"></a> -### The Local Driver - -When using the `local` driver, all file operations are relative to the `root` directory defined in your `filesystems` configuration file. By default, this value is set to the `storage/app` directory. Therefore, the following method would store a file in `storage/app/file.txt`: - - Storage::disk('local')->put('file.txt', 'Contents'); - -<a name="permissions"></a> -#### Permissions - -The `public` [visibility](#file-visibility) translates to `0755` for directories and `0644` for files. You can modify the permissions mappings in your `filesystems` configuration file: - - 'local' => [ - 'driver' => 'local', - 'root' => storage_path('app'), - 'permissions' => [ - 'file' => [ - 'public' => 0664, - 'private' => 0600, - ], - 'dir' => [ - 'public' => 0775, - 'private' => 0700, - ], - ], - ], - <a name="driver-prerequisites"></a> ### Driver Prerequisites <a name="composer-packages"></a> #### Composer Packages -Before using the SFTP or S3 drivers, you will need to install the appropriate package via Composer: +Before using the S3 or SFTP drivers, you will need to install the appropriate package via the Composer package manager: -- SFTP: `league/flysystem-sftp ~1.0` - Amazon S3: `league/flysystem-aws-s3-v3 ~1.0` +- SFTP: `league/flysystem-sftp ~1.0` -An absolute must for performance is to use a cached adapter. You will need an additional package for this: +In addition, you may choose to install a cached adapter for increased performance: - CachedAdapter: `league/flysystem-cached-adapter ~1.0` @@ -101,7 +86,7 @@ The S3 driver configuration information is located in your `config/filesystems.p <a name="ftp-driver-configuration"></a> #### 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: +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 configuration example below: 'ftp' => [ 'driver' => 'ftp', @@ -120,7 +105,7 @@ Laravel's Flysystem integrations works great with FTP; however, a sample configu <a name="sftp-driver-configuration"></a> #### SFTP Driver Configuration -Laravel's Flysystem integrations works great with SFTP; however, a sample configuration is not included with the framework's default `filesystems.php` configuration file. If you need to configure a SFTP filesystem, you may use the example configuration below: +Laravel's Flysystem integrations works great with SFTP; however, a sample configuration is not included with the framework's default `filesystems.php` configuration file. If you need to configure a SFTP filesystem, you may use the configuration example below: 'sftp' => [ 'driver' => 'sftp', @@ -129,8 +114,8 @@ Laravel's Flysystem integrations works great with SFTP; however, a sample config 'password' => 'your-password', // Settings for SSH key based authentication... - // 'privateKey' => '/path/to/privateKey', - // 'password' => 'encryption-password', + 'privateKey' => '/path/to/privateKey', + 'password' => 'encryption-password', // Optional SFTP Settings... // 'port' => 22, @@ -158,30 +143,34 @@ To enable caching for a given disk, you may add a `cache` directive to the disk' <a name="obtaining-disk-instances"></a> ## Obtaining Disk Instances -The `Storage` facade may be used to interact with any of your configured disks. For example, you may use the `put` method on the facade to store an avatar on the default disk. If you call methods on the `Storage` facade without first calling the `disk` method, the method call will automatically be passed to the default disk: +The `Storage` facade may be used to interact with any of your configured disks. For example, you may use the `put` method on the facade to store an avatar on the default disk. If you call methods on the `Storage` facade without first calling the `disk` method, the method will automatically be passed to the default disk: use Illuminate\Support\Facades\Storage; - Storage::put('avatars/1', $fileContents); + Storage::put('avatars/1', $content); If your application interacts with multiple disks, you may use the `disk` method on the `Storage` facade to work with files on a particular disk: - Storage::disk('s3')->put('avatars/1', $fileContents); + Storage::disk('s3')->put('avatars/1', $content); <a name="retrieving-files"></a> ## Retrieving Files -The `get` method may be used to retrieve the contents of a file. The raw string contents of the file will be returned by the method. Remember, all file paths should be specified relative to the "root" location configured for the disk: +The `get` method may be used to retrieve the contents of a file. The raw string contents of the file will be returned by the method. Remember, all file paths should be specified relative to the disk's "root" location: $contents = Storage::get('file.jpg'); The `exists` method may be used to determine if a file exists on the disk: - $exists = Storage::disk('s3')->exists('file.jpg'); + if (Storage::disk('s3')->exists('file.jpg')) { + // ... + } The `missing` method may be used to determine if a file is missing from the disk: - $missing = Storage::disk('s3')->missing('file.jpg'); + if (Storage::disk('s3')->missing('file.jpg')) { + // ... + } <a name="downloading-files"></a> ### Downloading Files @@ -195,7 +184,7 @@ The `download` method may be used to generate a response that forces the user's <a name="file-urls"></a> ### File URLs -You may use the `url` method to get the URL for the given file. If you are using the `local` driver, this will typically just prepend `/storage` to the given path and return a relative URL to the file. If you are using the `s3` driver, the fully qualified remote URL will be returned: +You may use the `url` method to get the URL for a given file. If you are using the `local` driver, this will typically just prepend `/storage` to the given path and return a relative URL to the file. If you are using the `s3` driver, the fully qualified remote URL will be returned: use Illuminate\Support\Facades\Storage; @@ -208,7 +197,9 @@ When using the `local` driver, all files that should be publicly accessible shou <a name="temporary-urls"></a> #### Temporary URLs -For files stored using the `s3` you may create a temporary URL to a given file using the `temporaryUrl` method. This method accepts a path and a `DateTime` instance specifying when the URL should expire: +Using the `temporaryUrl` method, you may create temporary URLs to files stored using the `s3` driver. This method accepts a path and a `DateTime` instance specifying when the URL should expire: + + use Illuminate\Support\Facades\Storage; $url = Storage::temporaryUrl( 'file.jpg', now()->addMinutes(5) @@ -228,7 +219,7 @@ If you need to specify additional [S3 request parameters](https://docs.aws.amazo <a name="url-host-customization"></a> #### URL Host Customization -If you would like to pre-define the host for file URLs generated using the `Storage` facade, you may add a `url` option to the disk's configuration array: +If you would like to pre-define the host for URLs generated using the `Storage` facade, you may add a `url` option to the disk's configuration array: 'public' => [ 'driver' => 'local', @@ -237,19 +228,10 @@ If you would like to pre-define the host for file URLs generated using the `Stor 'visibility' => 'public', ], -<a name="file-paths"></a> -### File Paths - -You may use the `path` method to get the path for a given file. If you are using the `local` driver, this will return the absolute path to the file. If you are using the `s3` driver, this method will return the relative path to the file in the S3 bucket: - - use Illuminate\Support\Facades\Storage; - - $path = Storage::path('file.jpg'); - <a name="file-metadata"></a> ### File Metadata -In addition to reading and writing files, Laravel can also provide information about the files themselves. For example, the `size` method may be used to get the size of the file in bytes: +In addition to reading and writing files, Laravel can also provide information about the files themselves. For example, the `size` method may be used to get the size of a file in bytes: use Illuminate\Support\Facades\Storage; @@ -259,10 +241,19 @@ The `lastModified` method returns the UNIX timestamp of the last time the file w $time = Storage::lastModified('file.jpg'); +<a name="file-paths"></a> +#### File Paths + +You may use the `path` method to get the path for a given file. If you are using the `local` driver, this will return the absolute path to the file. If you are using the `s3` driver, this method will return the relative path to the file in the S3 bucket: + + use Illuminate\Support\Facades\Storage; + + $path = Storage::path('file.jpg'); + <a name="storing-files"></a> ## Storing 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. Remember, all file paths should be specified relative to the "root" location configured for the disk: +The `put` method may be used to store file contents on a disk. You may also pass a PHP `resource` to the `put` method, which will use Flysystem's underlying stream support. Remember, all file paths should be specified relative to the "root" location configured for the disk: use Illuminate\Support\Facades\Storage; @@ -273,20 +264,20 @@ The `put` method may be used to store raw file contents on a disk. You may also <a name="automatic-streaming"></a> #### 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 an `Illuminate\Http\File` or `Illuminate\Http\UploadedFile` instance and will automatically stream the file to your desired location: +Streaming files to storage offers significantly reduced memory usage. 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 an `Illuminate\Http\File` or `Illuminate\Http\UploadedFile` instance and will automatically stream the file to your desired location: use Illuminate\Http\File; use Illuminate\Support\Facades\Storage; // Automatically generate a unique ID for file name... - Storage::putFile('photos', new File('/path/to/photo')); + $path = Storage::putFile('photos', new File('/path/to/photo')); // Manually specify a file name... - Storage::putFileAs('photos', new File('/path/to/photo'), 'photo.jpg'); + $path = 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 file's extension will be determined by examining the file's MIME type. 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. +There are a few important things to note about the `putFile` method. Note that we only specified a directory name and not a file name. By default, the `putFile` method will generate a unique ID to serve as the file name. The file's extension will be determined by examining the file's MIME type. 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: +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 Amazon S3 and would like the file to be publicly accessible via generated URLs: Storage::putFile('photos', new File('/path/to/photo'), 'public'); @@ -311,7 +302,7 @@ The `copy` method may be used to copy an existing file to a new location on the <a name="file-uploads"></a> ### File Uploads -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 makes it very easy to store uploaded files using the `store` method on an uploaded file instance. Call the `store` method with the path at which you wish to store the uploaded file: +In web applications, one of the most common use-cases for storing files is storing user uploaded files such as photos and documents. Laravel makes it very easy to store uploaded files using the `store` method on an uploaded file instance. Call the `store` method with the path at which you wish to store the uploaded file: <?php @@ -325,8 +316,8 @@ In web applications, one of the most common use-cases for storing files is stori /** * Update the avatar for the user. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function update(Request $request) { @@ -338,20 +329,20 @@ 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 generate a unique ID to serve as the file name. The file's extension will be determined by examining the file's MIME type. 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. -You may also call the `putFile` method on the `Storage` facade to perform the same file manipulation as the example above: +You may also call the `putFile` method on the `Storage` facade to perform the same file storage operation as the example above: $path = Storage::putFile('avatars', $request->file('avatar')); <a name="specifying-a-file-name"></a> #### Specifying A File Name -If you would not like a file name to be automatically assigned to your stored file, you may use the `storeAs` method, which receives the path, the file name, and the (optional) disk as its arguments: +If you do not want a file name to be automatically assigned to your stored file, you may use the `storeAs` method, which receives the path, the file name, and the (optional) disk as its arguments: $path = $request->file('avatar')->storeAs( 'avatars', $request->user()->id ); -You may also use the `putFileAs` method on the `Storage` facade, which will perform the same file manipulation as the example above: +You may also use the `putFileAs` method on the `Storage` facade, which will perform the same file storage operation as the example above: $path = Storage::putFileAs( 'avatars', $request->file('avatar'), $request->user()->id @@ -362,7 +353,7 @@ You may also use the `putFileAs` method on the `Storage` facade, which will perf <a name="specifying-a-disk"></a> #### 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: +By default, this uploaded file's `store` 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: $path = $request->file('avatar')->store( 'avatars/'.$request->user()->id, 's3' @@ -376,8 +367,8 @@ If you are using the `storeAs` method, you may pass the disk name as the third a 's3' ); -<a name="other-file-information"></a> -#### Other File Information +<a name="other-uploaded-file-information"></a> +#### Other Uploaded File Information If you would like to get original name of the uploaded file, you may do so using the `getClientOriginalName` method: @@ -392,7 +383,7 @@ The `extension` method may be used to get the file extension of the uploaded fil 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: +You can set the visibility when writing the file via the `put` method: use Illuminate\Support\Facades\Storage; @@ -414,10 +405,30 @@ When interacting with uploaded files, you may use the `storePublicly` and `store 's3' ); +<a name="local-files-and-visibility"></a> +#### Local Files & Visibility + +When using the `local` driver, `public` [visibility](#file-visibility) translates to `0755` permissions for directories and `0644` permissions for files. You can modify the permissions mappings in your application's `filesystems` configuration file: + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + 'permissions' => [ + 'file' => [ + 'public' => 0664, + 'private' => 0600, + ], + 'dir' => [ + 'public' => 0775, + 'private' => 0700, + ], + ], + ], + <a name="deleting-files"></a> ## Deleting Files -The `delete` method accepts a single filename or an array of files to remove from the disk: +The `delete` method accepts a single filename or an array of files to delete: use Illuminate\Support\Facades\Storage; @@ -429,7 +440,7 @@ If necessary, you may specify the disk that the file should be deleted from: use Illuminate\Support\Facades\Storage; - Storage::disk('s3')->delete('folder_path/file_name.jpg'); + Storage::disk('s3')->delete('path/file.jpg'); <a name="directories"></a> ## Directories @@ -452,7 +463,6 @@ The `directories` method returns an array of all the directories within a given $directories = Storage::directories($directory); - // Recursive... $directories = Storage::allDirectories($directory); <a name="create-a-directory"></a> @@ -472,13 +482,13 @@ Finally, the `deleteDirectory` method may be used to remove a directory and all <a name="custom-filesystems"></a> ## Custom Filesystems -Laravel's Flysystem integration provides drivers for several "drivers" out of the box; however, Flysystem is not limited to these and has adapters for many other storage systems. You can create a custom driver if you want to use one of these additional adapters in your Laravel application. +Laravel's Flysystem integration provides support for several "drivers" out of the box; however, Flysystem is not limited to these and has adapters for many other storage systems. You can create a custom driver if you want to use one of these additional adapters in your Laravel application. -In order to set up the custom filesystem you will need a Flysystem adapter. Let's add a community maintained Dropbox adapter to our project: +In order to define a custom filesystem you will need a Flysystem adapter. Let's add a community maintained Dropbox adapter to our project: composer require spatie/flysystem-dropbox -Next, you should create a [service provider](/docs/{{version}}/providers) such as `DropboxServiceProvider`. In the provider's `boot` method, you may use the `Storage` facade's `extend` method to define the custom driver: +Next, you can register the driver within the `boot` method of one of your application's [service providers](/docs/{{version}}/providers). To accomplish this, you should use the `extend` method of the `Storage` facade: <?php @@ -490,7 +500,7 @@ Next, you should create a [service provider](/docs/{{version}}/providers) such a use Spatie\Dropbox\Client as DropboxClient; use Spatie\FlysystemDropbox\DropboxAdapter; - class DropboxServiceProvider extends ServiceProvider + class AppServiceProvider extends ServiceProvider { /** * Register any application services. @@ -519,13 +529,6 @@ Next, you should create a [service provider](/docs/{{version}}/providers) such a } } -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. - -Next, register the service provider in your `config/app.php` configuration file: - - 'providers' => [ - // ... - App\Providers\DropboxServiceProvider::class, - ]; +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 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 and registered the extension's service provider, you may use the `dropbox` driver in your `config/filesystems.php` configuration file. From 48a1f8c35be4716d8765f7f7e143106d2701d721 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 16:15:15 -0600 Subject: [PATCH 112/274] wip --- helpers.md | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/helpers.md b/helpers.md index e01dc79ce5..95cba3419d 100644 --- a/helpers.md +++ b/helpers.md @@ -280,7 +280,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct <a name="method-array-accessible"></a> #### `Arr::accessible()` {#collection-method .first-collection-method} -The `Arr::accessible` method checks that the given value is array accessible: +The `Arr::accessible` method determines if the given value is array accessible: use Illuminate\Support\Arr; use Illuminate\Support\Collection; @@ -364,7 +364,7 @@ The `Arr::crossJoin` method cross joins the given arrays, returning a Cartesian <a name="method-array-divide"></a> #### `Arr::divide()` {#collection-method} -The `Arr::divide` method returns two arrays, one containing the keys, and the other containing the values of the given array: +The `Arr::divide` method returns two arrays: one containing the keys and the other containing the values of the given array: use Illuminate\Support\Arr; @@ -477,7 +477,7 @@ The `Arr::get` method retrieves a value from a deeply nested array using "dot" n // 100 -The `Arr::get` method also accepts a default value, which will be returned if the specific key is not found: +The `Arr::get` method also accepts a default value, which will be returned if the specified key is not present in the array: use Illuminate\Support\Arr; @@ -647,7 +647,13 @@ The `Arr::query` method converts the array into a query string: use Illuminate\Support\Arr; - $array = ['name' => 'Taylor', 'order' => ['column' => 'created_at', 'direction' => 'desc']]; + $array = [ + 'name' => 'Taylor', + 'order' => [ + 'column' => 'created_at', + 'direction' => 'desc' + ] + ]; Arr::query($array); @@ -666,7 +672,7 @@ The `Arr::random` method returns a random value from an array: // 4 - (retrieved randomly) -You may also specify the number of items to return as an optional second argument. Note that providing this argument will return an array, even if only one item is desired: +You may also specify the number of items to return as an optional second argument. Note that providing this argument will return an array even if only one item is desired: use Illuminate\Support\Arr; @@ -711,7 +717,7 @@ The `Arr::sort` method sorts an array by its values: // ['Chair', 'Desk', 'Table'] -You may also sort the array by the results of the given closure: +You may also sort the array by the results of a given closure: use Illuminate\Support\Arr; @@ -736,7 +742,7 @@ You may also sort the array by the results of the given closure: <a name="method-array-sort-recursive"></a> #### `Arr::sortRecursive()` {#collection-method} -The `Arr::sortRecursive` method recursively sorts an array using the `sort` function for numeric sub=arrays and `ksort` for associative subarrays: +The `Arr::sortRecursive` method recursively sorts an array using the `sort` function for numerically indexed sub-arrays and the `ksort` function for associative sub-arrays: use Illuminate\Support\Arr; @@ -774,7 +780,7 @@ The `Arr::where` method filters an array using the given closure: <a name="method-array-wrap"></a> #### `Arr::wrap()` {#collection-method} -The `Arr::wrap` method wraps the given value in an array. If the given value is already an array it will not be changed: +The `Arr::wrap` method wraps the given value in an array. If the given value is already an array it be returned without modification: use Illuminate\Support\Arr; @@ -784,13 +790,11 @@ The `Arr::wrap` method wraps the given value in an array. If the given value is // ['Laravel'] -If the given value is null, an empty array will be returned: +If the given value is `null`, an empty array will be returned: use Illuminate\Support\Arr; - $nothing = null; - - $array = Arr::wrap($nothing); + $array = Arr::wrap(null); // [] @@ -868,7 +872,7 @@ The `data_set` function sets a value within a nested array or object using "dot" // ['products' => ['desk' => ['price' => 200]]] -This function also accepts wildcards and will set values on the target accordingly: +This function also accepts wildcards using asterisks and will set values on the target accordingly: $data = [ 'products' => [ @@ -888,11 +892,11 @@ This function also accepts wildcards and will set values on the target according ] */ -By default, any existing values are overwritten. If you wish to only set a value if it doesn't exist, you may pass `false` as the fourth argument: +By default, any existing values are overwritten. If you wish to only set a value if it doesn't exist, you may pass `false` as the fourth argument to the function: $data = ['products' => ['desk' => ['price' => 100]]]; - data_set($data, 'products.desk.price', 200, false); + data_set($data, 'products.desk.price', 200, $overwrite = false); // ['products' => ['desk' => ['price' => 100]]] From 02f6b94038aa68f8fd4181a1f0a92c4ab19a13f5 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Wed, 18 Nov 2020 16:51:01 -0600 Subject: [PATCH 113/274] wip --- helpers.md | 145 +++++++++++++++++++++++++++-------------------------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/helpers.md b/helpers.md index 95cba3419d..83e7df77c5 100644 --- a/helpers.md +++ b/helpers.md @@ -928,7 +928,7 @@ The `last` function returns the last element in the given array: <a name="method-app-path"></a> #### `app_path()` {#collection-method} -The `app_path` function returns the fully qualified path to the `app` directory. You may also use the `app_path` function to generate a fully qualified path to a file relative to the application directory: +The `app_path` function returns the fully qualified path to your application's `app` directory. You may also use the `app_path` function to generate a fully qualified path to a file relative to the application directory: $path = app_path(); @@ -937,7 +937,7 @@ The `app_path` function returns the fully qualified path to the `app` directory. <a name="method-base-path"></a> #### `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 project root directory: +The `base_path` function returns the fully qualified path to your application's root directory. 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(); @@ -946,7 +946,7 @@ The `base_path` function returns the fully qualified path to the project root. Y <a name="method-config-path"></a> #### `config_path()` {#collection-method} -The `config_path` function returns the fully qualified path to the `config` directory. You may also use the `config_path` function to generate a fully qualified path to a given file within the application's configuration directory: +The `config_path` function returns the fully qualified path to your application's `config` directory. You may also use the `config_path` function to generate a fully qualified path to a given file within the application's configuration directory: $path = config_path(); @@ -955,7 +955,7 @@ The `config_path` function returns the fully qualified path to the `config` dire <a name="method-database-path"></a> #### `database_path()` {#collection-method} -The `database_path` function returns the fully qualified path to the `database` directory. You may also use the `database_path` function to generate a fully qualified path to a given file within the database directory: +The `database_path` function returns the fully qualified path to your application's `database` directory. You may also use the `database_path` function to generate a fully qualified path to a given file within the database directory: $path = database_path(); @@ -971,7 +971,7 @@ The `mix` function returns the path to a [versioned Mix file](/docs/{{version}}/ <a name="method-public-path"></a> #### `public_path()` {#collection-method} -The `public_path` function returns the fully qualified path to the `public` directory. You may also use the `public_path` function to generate a fully qualified path to a given file within the public directory: +The `public_path` function returns the fully qualified path to your application's `public` directory. You may also use the `public_path` function to generate a fully qualified path to a given file within the public directory: $path = public_path(); @@ -980,7 +980,7 @@ The `public_path` function returns the fully qualified path to the `public` dire <a name="method-resource-path"></a> #### `resource_path()` {#collection-method} -The `resource_path` function returns the fully qualified path to the `resources` directory. You may also use the `resource_path` function to generate a fully qualified path to a given file within the resources directory: +The `resource_path` function returns the fully qualified path to your application's `resources` directory. You may also use the `resource_path` function to generate a fully qualified path to a given file within the resources directory: $path = resource_path(); @@ -989,7 +989,7 @@ The `resource_path` function returns the fully qualified path to the `resources` <a name="method-storage-path"></a> #### `storage_path()` {#collection-method} -The `storage_path` function returns the fully qualified path to the `storage` directory. You may also use the `storage_path` function to generate a fully qualified path to a given file within the storage directory: +The `storage_path` function returns the fully qualified path to your application's `storage` directory. You may also use the `storage_path` function to generate a fully qualified path to a given file within the storage directory: $path = storage_path(); @@ -1118,7 +1118,7 @@ The `Str::camel` method converts the given string to `camelCase`: <a name="method-str-contains"></a> #### `Str::contains()` {#collection-method} -The `Str::contains` method determines if the given string contains the given value (case sensitive): +The `Str::contains` method determines if the given string contains the given value. This method is case sensitive: use Illuminate\Support\Str; @@ -1126,7 +1126,7 @@ The `Str::contains` method determines if the given string contains the given val // true -You may also pass an array of values to determine if the given string contains any of the values: +You may also pass an array of values to determine if the given string contains any of the values in the array: use Illuminate\Support\Str; @@ -1137,7 +1137,7 @@ You may also pass an array of values to determine if the given string contains a <a name="method-str-contains-all"></a> #### `Str::containsAll()` {#collection-method} -The `Str::containsAll` method determines if the given string contains all array values: +The `Str::containsAll` method determines if the given string contains all of the values in a given array: use Illuminate\Support\Str; @@ -1157,7 +1157,7 @@ The `Str::endsWith` method determines if the given string ends with the given va // true -You may also pass an array of values to determine if the given string ends with any of the given values: +You may also pass an array of values to determine if the given string ends with any of the values in the array: use Illuminate\Support\Str; @@ -1172,7 +1172,7 @@ You may also pass an array of values to determine if the given string ends with <a name="method-str-finish"></a> #### `Str::finish()` {#collection-method} -The `Str::finish` method adds a single instance of the given value to a string if it does not already end with the value: +The `Str::finish` method adds a single instance of the given value to a string if it does not already end with that value: use Illuminate\Support\Str; @@ -1187,7 +1187,7 @@ The `Str::finish` method adds a single instance of the given value to a string i <a name="method-str-is"></a> #### `Str::is()` {#collection-method} -The `Str::is` method determines if a given string matches a given pattern. Asterisks may be used to indicate wildcards: +The `Str::is` method determines if a given string matches a given pattern. Asterisks may be used as wildcard values: use Illuminate\Support\Str; @@ -1254,7 +1254,7 @@ The `Str::length` method returns the length of the given string: <a name="method-str-limit"></a> #### `Str::limit()` {#collection-method} -The `Str::limit` method truncates the given string at the specified length: +The `Str::limit` method truncates the given string to the specified length: use Illuminate\Support\Str; @@ -1262,7 +1262,7 @@ The `Str::limit` method truncates the given string at the specified length: // The quick brown fox... -You may also pass a third argument to change the string that will be appended to the end: +You may pass a third argument to the method to change the string that will be appended to the end of the truncated string: use Illuminate\Support\Str; @@ -1284,7 +1284,7 @@ The `Str::lower` method converts the given string to lowercase: <a name="method-str-ordered-uuid"></a> #### `Str::orderedUuid()` {#collection-method} -The `Str::orderedUuid` method generates a "timestamp first" UUID that may be efficiently stored in an indexed database column: +The `Str::orderedUuid` method generates a "timestamp first" UUID that may be efficiently stored in an indexed database column. Each UUID that is generated using this method will be sorted after UUIDs previously generated using the method: use Illuminate\Support\Str; @@ -1293,7 +1293,7 @@ The `Str::orderedUuid` method generates a "timestamp first" UUID that may be eff <a name="method-str-padboth"></a> #### `Str::padBoth()` {#collection-method} -The `Str::padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another: +The `Str::padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another string until the final string reaches a desired length: use Illuminate\Support\Str; @@ -1308,7 +1308,7 @@ The `Str::padBoth` method wraps PHP's `str_pad` function, padding both sides of <a name="method-str-padleft"></a> #### `Str::padLeft()` {#collection-method} -The `Str::padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another: +The `Str::padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another string until the final string reaches a desired length: use Illuminate\Support\Str; @@ -1323,7 +1323,7 @@ The `Str::padLeft` method wraps PHP's `str_pad` function, padding the left side <a name="method-str-padright"></a> #### `Str::padRight()` {#collection-method} -The `Str::padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another: +The `Str::padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another string until the final string reaches a desired length: use Illuminate\Support\Str; @@ -1338,7 +1338,7 @@ The `Str::padRight` method wraps PHP's `str_pad` function, padding the right sid <a name="method-str-plural"></a> #### `Str::plural()` {#collection-method} -The `Str::plural` method converts a single word string to its plural form. This function currently only supports the English language: +The `Str::plural` method converts a singular word string to its plural form. This function currently only supports the English language: use Illuminate\Support\Str; @@ -1446,7 +1446,7 @@ The `Str::snake` method converts the given string to `snake_case`: <a name="method-str-start"></a> #### `Str::start()` {#collection-method} -The `Str::start` method adds a single instance of the given value to a string if it does not already start with the value: +The `Str::start` method adds a single instance of the given value to a string if it does not already start with that value: use Illuminate\Support\Str; @@ -1536,7 +1536,7 @@ The `Str::uuid` method generates a UUID (version 4): <a name="method-str-words"></a> #### `Str::words()` {#collection-method} -The `Str::words` method limits the number of words in a string: +The `Str::words` method limits the number of words in a string. An additional string may be passed to this method via its third argument to specify which string should be appended to the end of the truncated string: use Illuminate\Support\Str; @@ -1666,7 +1666,7 @@ The `camel` method converts the given string to `camelCase`: <a name="method-fluent-str-contains"></a> #### `contains` {#collection-method} -The `contains` method determines if the given string contains the given value (case sensitive): +The `contains` method determines if the given string contains the given value. This method is case sensitive: use Illuminate\Support\Str; @@ -1674,7 +1674,7 @@ The `contains` method determines if the given string contains the given value (c // true -You may also pass an array of values to determine if the given string contains any of the values: +You may also pass an array of values to determine if the given string contains any of the values in the array: use Illuminate\Support\Str; @@ -1685,7 +1685,7 @@ You may also pass an array of values to determine if the given string contains a <a name="method-fluent-str-contains-all"></a> #### `containsAll` {#collection-method} -The `containsAll` method determines if the given string contains all array values: +The `containsAll` method determines if the given string contains all of the values in the given array: use Illuminate\Support\Str; @@ -1704,7 +1704,7 @@ The `dirname` method returns the parent directory portion of the given string: // '/foo/bar' -Optionally, You may specify how many directory levels you wish to trim from the string: +If necessary, you may specify how many directory levels you wish to trim from the string: use Illuminate\Support\Str; @@ -1723,7 +1723,7 @@ The `endsWith` method determines if the given string ends with the given value: // true -You may also pass an array of values to determine if the given string ends with any of the given values: +You may also pass an array of values to determine if the given string ends with any of the values in the array: use Illuminate\Support\Str; @@ -1760,7 +1760,7 @@ The `explode` method splits the string by the given delimiter and returns a coll <a name="method-fluent-str-finish"></a> #### `finish` {#collection-method} -The `finish` method adds a single instance of the given value to a string if it does not already end with the value: +The `finish` method adds a single instance of the given value to a string if it does not already end with that value: use Illuminate\Support\Str; @@ -1775,7 +1775,7 @@ The `finish` method adds a single instance of the given value to a string if it <a name="method-fluent-str-is"></a> #### `is` {#collection-method} -The `is` method determines if a given string matches a given pattern. Asterisks may be used to indicate wildcards: +The `is` method determines if a given string matches a given pattern. Asterisks may be used as wildcard values use Illuminate\Support\Str; @@ -1858,7 +1858,7 @@ The `length` method returns the length of the given string: <a name="method-fluent-str-limit"></a> #### `limit` {#collection-method} -The `limit` method truncates the given string at the specified length: +The `limit` method truncates the given string to the specified length: use Illuminate\Support\Str; @@ -1866,7 +1866,7 @@ The `limit` method truncates the given string at the specified length: // The quick brown fox... -You may also pass a second argument to change the string that will be appended to the end: +You may also pass a second argument to change the string that will be appended to the end of the truncated string: use Illuminate\Support\Str; @@ -1888,7 +1888,7 @@ The `lower` method converts the given string to lowercase: <a name="method-fluent-str-ltrim"></a> #### `ltrim` {#collection-method} -The `ltrim` method left trims the given string: +The `ltrim` method trims the left side of the string: use Illuminate\Support\Str; @@ -1939,7 +1939,7 @@ If no matches are found, an empty collection will be returned. <a name="method-fluent-str-padboth"></a> #### `padBoth` {#collection-method} -The `padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another: +The `padBoth` method wraps PHP's `str_pad` function, padding both sides of a string with another string until the final string reaches the desired length: use Illuminate\Support\Str; @@ -1954,7 +1954,7 @@ The `padBoth` method wraps PHP's `str_pad` function, padding both sides of a str <a name="method-fluent-str-padleft"></a> #### `padLeft` {#collection-method} -The `padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another: +The `padLeft` method wraps PHP's `str_pad` function, padding the left side of a string with another string until the final string reaches the desired length: use Illuminate\Support\Str; @@ -1969,7 +1969,7 @@ The `padLeft` method wraps PHP's `str_pad` function, padding the left side of a <a name="method-fluent-str-padright"></a> #### `padRight` {#collection-method} -The `padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another: +The `padRight` method wraps PHP's `str_pad` function, padding the right side of a string with another string until the final string reaches the desired length: use Illuminate\Support\Str; @@ -1984,7 +1984,7 @@ The `padRight` method wraps PHP's `str_pad` function, padding the right side of <a name="method-fluent-str-plural"></a> #### `plural` {#collection-method} -The `plural` method converts a single word string to its plural form. This function currently only supports the English language: +The `plural` method converts a singular word string to its plural form. This function currently only supports the English language: use Illuminate\Support\Str; @@ -2068,7 +2068,7 @@ The `replaceLast` method replaces the last occurrence of a given value in a stri <a name="method-fluent-str-replace-matches"></a> #### `replaceMatches` {#collection-method} -The `replaceMatches` method replaces all portions of a string matching a given pattern with the given replacement string: +The `replaceMatches` method replaces all portions of a string matching a pattern with the given replacement string: use Illuminate\Support\Str; @@ -2076,7 +2076,7 @@ The `replaceMatches` method replaces all portions of a string matching a given p // '15015551000' -The `replaceMatches` method also accepts a closure that will be invoked with each portion of the string matching the given party, allowing you to perform the replacement logic within the closure and return the replaced value: +The `replaceMatches` method also accepts a closure that will be invoked with each portion of the string matching the given pattern, allowing you to perform the replacement logic within the closure and return the replaced value: use Illuminate\Support\Str; @@ -2089,7 +2089,7 @@ The `replaceMatches` method also accepts a closure that will be invoked with eac <a name="method-fluent-str-rtrim"></a> #### `rtrim` {#collection-method} -The `rtrim` method right trims the given string: +The `rtrim` method trims the right side of the given string: use Illuminate\Support\Str; @@ -2152,7 +2152,7 @@ The `split` method splits a string into a collection using a regular expression: <a name="method-fluent-str-start"></a> #### `start` {#collection-method} -The `start` method adds a single instance of the given value to a string if it does not already start with the value: +The `start` method adds a single instance of the given value to a string if it does not already start with that value: use Illuminate\Support\Str; @@ -2252,7 +2252,7 @@ The `upper` method converts the given string to uppercase: <a name="method-fluent-str-when"></a> #### `when` {#collection-method} -The `when` method invokes the given closure if a given condition is true. The closure will receive the fluent string instance: +The `when` method invokes the given closure if a given condition is `true`. The closure will receive the fluent string instance: use Illuminate\Support\Str; @@ -2281,7 +2281,7 @@ The `whenEmpty` method invokes the given closure if the string is empty. If the <a name="method-fluent-str-words"></a> #### `words` {#collection-method} -The `words` method limits the number of words in a string: +The `words` method limits the number of words in a string. If necessary, you may specify an additional string that will be appended to the truncated string: use Illuminate\Support\Str; @@ -2297,6 +2297,8 @@ The `words` method limits the number of words in a string: The `action` function generates a URL for the given controller action: + use App\Http\Controllers\HomeController; + $url = action([HomeController::class, 'index']); If the method accepts route parameters, you may pass them as the second argument to the method: @@ -2310,7 +2312,7 @@ The `asset` function generates a URL for an asset using the current scheme of th $url = asset('img/photo.jpg'); -You can configure the asset URL host by setting the `ASSET_URL` variable in your `.env` file. This can be useful if you host your assets on an external service like Amazon S3: +You can configure the asset URL host by setting the `ASSET_URL` variable in your `.env` file. This can be useful if you host your assets on an external service like Amazon S3 or another CDN: // ASSET_URL=http://example.com/assets @@ -2319,17 +2321,17 @@ You can configure the asset URL host by setting the `ASSET_URL` variable in your <a name="method-route"></a> #### `route()` {#collection-method} -The `route` function generates a URL for the given named route: +The `route` function generates a URL for a given [named route](/docs/{{version}}/routing#named-routes): - $url = route('routeName'); + $url = route('route.name'); -If the route accepts parameters, you may pass them as the second argument to the method: +If the route accepts parameters, you may pass them as the second argument to the function: - $url = route('routeName', ['id' => 1]); + $url = route('route.name', ['id' => 1]); -By default, the `route` function generates an absolute URL. If you wish to generate a relative URL, you may pass `false` as the third argument: +By default, the `route` function generates an absolute URL. If you wish to generate a relative URL, you may pass `false` as the third argument to the function: - $url = route('routeName', ['id' => 1], false); + $url = route('route.name', ['id' => 1], false); <a name="method-secure-asset"></a> #### `secure_asset()` {#collection-method} @@ -2341,7 +2343,7 @@ The `secure_asset` function generates a URL for an asset using HTTPS: <a name="method-secure-url"></a> #### `secure_url()` {#collection-method} -The `secure_url` function generates a fully qualified HTTPS URL to the given path: +The `secure_url` function generates a fully qualified HTTPS URL to the given path. Additional URL segments may be passed in the function's second argument: $url = secure_url('user/profile'); @@ -2374,7 +2376,7 @@ The `abort` function throws [an HTTP exception](/docs/{{version}}/errors#http-ex abort(403); -You may also provide the exception's message and custom response headers that should be sent to the browser: +You may also provide the exception's message and custom HTTP response headers that should be sent to the browser: abort(403, 'Unauthorized.', $headers); @@ -2385,7 +2387,7 @@ The `abort_if` function throws an HTTP exception if a given boolean expression e abort_if(! Auth::user()->isAdmin(), 403); -Like the `abort` method, you may also provide the exception's response text as the third argument and an array of custom response headers as the fourth argument. +Like the `abort` method, you may also provide the exception's response text as the third argument and an array of custom response headers as the fourth argument to the function. <a name="method-abort-unless"></a> #### `abort_unless()` {#collection-method} @@ -2394,7 +2396,7 @@ The `abort_unless` function throws an HTTP exception if a given boolean expressi abort_unless(Auth::user()->isAdmin(), 403); -Like the `abort` method, you may also provide the exception's response text as the third argument and an array of custom response headers as the fourth argument. +Like the `abort` method, you may also provide the exception's response text as the third argument and an array of custom response headers as the fourth argument to the function. <a name="method-app"></a> #### `app()` {#collection-method} @@ -2410,7 +2412,7 @@ You may pass a class or interface name to resolve it from the container: <a name="method-auth"></a> #### `auth()` {#collection-method} -The `auth` function returns an [authenticator](/docs/{{version}}/authentication) instance. You may use it instead of the `Auth` facade for convenience: +The `auth` function returns an [authenticator](/docs/{{version}}/authentication) instance. You may use it as an alternative to the `Auth` facade: $user = auth()->user(); @@ -2423,21 +2425,21 @@ If needed, you may specify which guard instance you would like to access: The `back` function generates a [redirect HTTP response](/docs/{{version}}/responses#redirects) to the user's previous location: - return back($status = 302, $headers = [], $fallback = false); + return back($status = 302, $headers = [], $fallback = '/'); return back(); <a name="method-bcrypt"></a> #### `bcrypt()` {#collection-method} -The `bcrypt` function [hashes](/docs/{{version}}/hashing) the given value using Bcrypt. You may use it as an alternative to the `Hash` facade: +The `bcrypt` function [hashes](/docs/{{version}}/hashing) the given value using Bcrypt. You may use this function as an alternative to the `Hash` facade: $password = bcrypt('my-secret-password'); <a name="method-blank"></a> #### `blank()` {#collection-method} -The `blank` function returns whether the given value is "blank": +The `blank` function determines whether the given value is "blank": blank(''); blank(' '); @@ -2461,6 +2463,8 @@ The `broadcast` function [broadcasts](/docs/{{version}}/broadcasting) the given broadcast(new UserRegistered($user)); + broadcast(new UserRegistered($user))->toOthers(); + <a name="method-cache"></a> #### `cache()` {#collection-method} @@ -2499,7 +2503,7 @@ The `config` function gets the value of a [configuration](/docs/{{version}}/conf $value = config('app.timezone', $default); -You may set configuration variables at runtime by passing an array of key / value pairs: +You may set configuration variables at runtime by passing an array of key / value pairs. However, note that this function only affects the configuration value for the current request and does not update your actual configuration values: config(['app.debug' => true]); @@ -2567,7 +2571,6 @@ The `env` function retrieves the value of an [environment variable](/docs/{{vers $env = env('APP_ENV'); - // Returns 'production' if APP_ENV is not set... $env = env('APP_ENV', 'production'); > {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. Once the configuration has been cached, the `.env` file will not be loaded and all calls to the `env` function will return `null`. @@ -2582,7 +2585,7 @@ The `event` function dispatches the given [event](/docs/{{version}}/events) to i <a name="method-filled"></a> #### `filled()` {#collection-method} -The `filled` function returns whether the given value is not "blank": +The `filled` function determines whether the given value is not "blank": filled(0); filled(true); @@ -2602,7 +2605,7 @@ For the inverse of `filled`, see the [`blank`](#method-blank) method. <a name="method-info"></a> #### `info()` {#collection-method} -The `info` function will write information to the [log](/docs/{{version}}/logging): +The `info` function will write information to your application's [log](/docs/{{version}}/logging): info('Some helpful information!'); @@ -2677,7 +2680,7 @@ The `policy` method retrieves a [policy](/docs/{{version}}/authorization#creatin The `redirect` function returns a [redirect HTTP response](/docs/{{version}}/responses#redirects), or returns the redirector instance if called with no arguments: - return redirect($to = null, $status = 302, $headers = [], $secure = null); + return redirect($to = null, $status = 302, $headers = [], $https = null); return redirect('/home'); @@ -2693,7 +2696,7 @@ The `report` function will report an exception using your [exception handler](/d <a name="method-request"></a> #### `request()` {#collection-method} -The `request` function returns the current [request](/docs/{{version}}/requests) instance or obtains an input item: +The `request` function returns the current [request](/docs/{{version}}/requests) instance or obtains an input field's value from the current request: $request = request(); @@ -2723,7 +2726,7 @@ You may also pass a second argument to the `rescue` function. This argument will <a name="method-resolve"></a> #### `resolve()` {#collection-method} -The `resolve` function resolves a given class or interface name to its instance using the [service container](/docs/{{version}}/container): +The `resolve` function resolves a given class or interface name to an instance using the [service container](/docs/{{version}}/container): $api = resolve('HelpSpot\API'); @@ -2796,7 +2799,7 @@ The `throw_if` function throws the given exception if a given boolean expression throw_if( ! Auth::user()->isAdmin(), AuthorizationException::class, - 'You are not allowed to access this page' + 'You are not allowed to access this page.' ); <a name="method-throw-unless"></a> @@ -2809,7 +2812,7 @@ The `throw_unless` function throws the given exception if a given boolean expres throw_unless( Auth::user()->isAdmin(), AuthorizationException::class, - 'You are not allowed to access this page' + 'You are not allowed to access this page.' ); <a name="method-today"></a> @@ -2829,7 +2832,7 @@ The `trait_uses_recursive` function returns all traits used by a trait: <a name="method-transform"></a> #### `transform()` {#collection-method} -The `transform` function executes a closure on a given value if the value is not [blank](#method-blank) and returns the result of the closure: +The `transform` function executes a closure on a given value if the value is not [blank](#method-blank) and then returns the return value of the closure: $callback = function ($value) { return $value * 2; @@ -2839,7 +2842,7 @@ The `transform` function executes a closure on a given value if the value is not // 10 -A default value or closure may also be passed as the third parameter to the method. This value will be returned if the given value is blank: +A default value or closure may be passed as the third argument to the function. This value will be returned if the given value is blank: $result = transform(null, $callback, 'The value is blank'); @@ -2848,14 +2851,14 @@ A default value or closure may also be passed as the third parameter to the meth <a name="method-validator"></a> #### `validator()` {#collection-method} -The `validator` function creates a new [validator](/docs/{{version}}/validation) instance with the given arguments. You may use it instead of the `Validator` facade for convenience: +The `validator` function creates a new [validator](/docs/{{version}}/validation) instance with the given arguments. You may use it as an alternative to the `Validator` facade: $validator = validator($data, $rules, $messages); <a name="method-value"></a> #### `value()` {#collection-method} -The `value` function returns 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: +The `value` function returns the value it is given. However, if you pass a closure to the function, the closure will be executed and its returned value will be returned: $result = value(true); @@ -2877,7 +2880,7 @@ The `view` function retrieves a [view](/docs/{{version}}/views) instance: <a name="method-with"></a> #### `with()` {#collection-method} -The `with` function returns the value it is given. If a closure is passed as the second argument to the function, the closure will be executed and its result will be returned: +The `with` function returns the value it is given. If a closure is passed as the second argument to the function, the closure will be executed and its returned value will be returned: $callback = function ($value) { return (is_numeric($value)) ? $value * 2 : 0; From e08469f1d6c566b306012cbf1df67f134f7dfc83 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 11:26:01 -0600 Subject: [PATCH 114/274] wip --- http-client.md | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/http-client.md b/http-client.md index 0624a8dcbe..8825b5cc62 100644 --- a/http-client.md +++ b/http-client.md @@ -18,14 +18,14 @@ Laravel provides an expressive, minimal API around the [Guzzle HTTP client](http://docs.guzzlephp.org/en/stable/), allowing you to quickly make outgoing HTTP requests to communicate with other web applications. Laravel's wrapper around Guzzle is focused on its most common use cases and a wonderful developer experience. -Before getting started, you should ensure that you have installed the Guzzle package as a dependency of your application. By default, Laravel automatically includes this dependency: +Before getting started, you should ensure that you have installed the Guzzle package as a dependency of your application. By default, Laravel automatically includes this dependency. However, if you have previously removed the package, you may install it again via Composer: composer require guzzlehttp/guzzle <a name="making-requests"></a> ## Making Requests -To make requests, you may use the `get`, `post`, `put`, `patch`, and `delete` methods. First, let's examine how to make a basic `GET` request: +To make requests, you may use the `get`, `post`, `put`, `patch`, and `delete` methods provided by the `Http` facade. First, let's examine how to make a basic `GET` request to another URL: use Illuminate\Support\Facades\Http; @@ -51,7 +51,9 @@ The `Illuminate\Http\Client\Response` object also implements the PHP `ArrayAcces <a name="request-data"></a> ### Request Data -Of course, it is common when using `POST`, `PUT`, and `PATCH` to send additional data with your request. So, these methods accept an array of data as their second argument. By default, data will be sent using the `application/json` content type: +Of course, it is common when making `POST`, `PUT`, and `PATCH` requests to send additional data with your request, so these methods accept an array of data as their second argument. By default, data will be sent using the `application/json` content type: + + use Illuminate\Support\Facades\Http; $response = Http::post('http://example.com/users', [ 'name' => 'Steve', @@ -81,7 +83,7 @@ If you would like to send data using the `application/x-www-form-urlencoded` con <a name="sending-a-raw-request-body"></a> #### Sending A Raw Request Body -You may use the `withBody` method if you would like to provide a raw request body when making a request: +You may use the `withBody` method if you would like to provide a raw request body when making a request. The content type may be provided via the method's second argument: $response = Http::withBody( base64_encode($photo), 'image/jpeg' @@ -90,13 +92,13 @@ You may use the `withBody` method if you would like to provide a raw request bod <a name="multi-part-requests"></a> #### Multi-Part Requests -If you would like to send files as multi-part requests, you should call the `attach` method before making your request. This method accepts the name of the file and its contents. Optionally, you may provide a third argument which will be considered the file's filename: +If you would like to send files as multi-part requests, you should call the `attach` method before making your request. This method accepts the name of the file and its contents. If needed, you may provide a third argument which will be considered the file's filename: $response = Http::attach( 'attachment', file_get_contents('photo.jpg'), 'photo.jpg' )->post('http://example.com/attachments'); -Instead of passing the raw contents of a file, you may also pass a stream resource: +Instead of passing the raw contents of a file, you may pass a stream resource: $photo = fopen('photo.jpg', 'r'); @@ -130,7 +132,7 @@ You may specify basic and digest authentication credentials using the `withBasic <a name="bearer-tokens"></a> #### Bearer Tokens -If you would like to quickly add an `Authorization` bearer token header to the request, you may use the `withToken` method: +If you would like to quickly add a bearer token to the request's `Authorization` header, you may use the `withToken` method: $response = Http::withToken('token')->post(...); @@ -146,7 +148,7 @@ If the given timeout is exceeded, an instance of `Illuminate\Http\Client\Connect <a name="retries"></a> ### Retries -If you would like HTTP client to automatically retry the request if a client or server error occurs, you may use the `retry` method. The `retry` method accepts two arguments: the number of times the request should be attempted and the number of milliseconds that Laravel should wait in between attempts: +If you would like HTTP client to automatically retry the request if a client or server error occurs, you may use the `retry` method. The `retry` method accepts two arguments: the maximum number of times the request should be attempted and the number of milliseconds that Laravel should wait in between attempts: $response = Http::retry(3, 100)->post(...); @@ -157,10 +159,10 @@ If all of the requests fail, an instance of `Illuminate\Http\Client\RequestExcep Unlike Guzzle's default behavior, Laravel's HTTP client wrapper does not throw exceptions on client or server errors (`400` and `500` level responses from servers). You may determine if one of these errors was returned using the `successful`, `clientError`, or `serverError` methods: - // Determine if the status code was >= 200 and < 300... + // Determine if the status code is >= 200 and < 300... $response->successful(); - // Determine if the status code was >= 400... + // Determine if the status code is >= 400... $response->failed(); // Determine if the response has a 400 level status code... @@ -172,7 +174,7 @@ Unlike Guzzle's default behavior, Laravel's HTTP client wrapper does not throw e <a name="throwing-exceptions"></a> #### Throwing Exceptions -If you have a response instance and would like to throw an instance of `Illuminate\Http\Client\RequestException` if the response is a client or server error, you may use the `throw` method: +If you have a response instance and would like to throw an instance of `Illuminate\Http\Client\RequestException` if the response status code indicates a client or server error, you may use the `throw` method: $response = Http::post(...); @@ -223,14 +225,14 @@ For example, to instruct the HTTP client to return empty, `200` status code resp <a name="faking-specific-urls"></a> #### Faking Specific URLs -Alternatively, you may pass an array to the `fake` method. The array's keys should represent URL patterns that you wish to fake and their associated responses. The `*` character may be used as a wildcard character. Any requests made to URLs that have not been faked will actually be executed. You may use the `response` method to construct stub / fake responses for these endpoints: +Alternatively, you may pass an array to the `fake` method. The array's keys should represent URL patterns that you wish to fake and their associated responses. The `*` character may be used as a wildcard character. Any requests made to URLs that have not been faked will actually be executed. You may use the `Http` facade's `response` method to construct stub / fake responses for these endpoints: Http::fake([ // Stub a JSON response for GitHub endpoints... - 'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']), + 'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers), // Stub a string response for Google endpoints... - 'google.com/*' => Http::response('Hello World', 200, ['Headers']), + 'google.com/*' => Http::response('Hello World', 200, $headers), ]); If you would like to specify a fallback URL pattern that will stub all unmatched URLs, you may use a single `*` character: @@ -275,7 +277,7 @@ If you would like to fake a sequence of responses but do not need to specify a s <a name="fake-callback"></a> #### Fake Callback -If you require more complicated logic to determine what responses to return for certain endpoints, you may pass a callback to the `fake` method. This callback will receive an instance of `Illuminate\Http\Client\Request` and should return a response instance: +If you require more complicated logic to determine what responses to return for certain endpoints, you may pass a closure to the `fake` method. This closure will receive an instance of `Illuminate\Http\Client\Request` and should return a response instance. Within your closure, you may perform whatever logic is necessary to determine what type of response to return: Http::fake(function ($request) { return Http::response('Hello World', 200); @@ -286,7 +288,10 @@ If you require more complicated logic to determine what responses to return for When faking responses, you may occasionally wish to inspect the requests the client receives in order to make sure your application is sending the correct data or headers. You may accomplish this by calling the `Http::assertSent` method after calling `Http::fake`. -The `assertSent` method accepts a callback which will be given an `Illuminate\Http\Client\Request` instance and should return a boolean value indicating if the request matches your expectations. In order for the test to pass, at least one request must have been issued matching the given expectations: +The `assertSent` method accepts a closure which will receive an `Illuminate\Http\Client\Request` instance and should return a boolean value indicating if the request matches your expectations. In order for the test to pass, at least one request must have been issued matching the given expectations: + + use Illuminate\Http\Client\Request; + use Illuminate\Support\Facades\Http; Http::fake(); @@ -297,7 +302,7 @@ The `assertSent` method accepts a callback which will be given an `Illuminate\Ht 'role' => 'Developer', ]); - Http::assertSent(function ($request) { + Http::assertSent(function (Request $request) { return $request->hasHeader('X-First', 'foo') && $request->url() == 'http://example.com/users' && $request['name'] == 'Taylor' && @@ -306,6 +311,9 @@ The `assertSent` method accepts a callback which will be given an `Illuminate\Ht If needed, you may assert that a specific request was not sent using the `assertNotSent` method: + use Illuminate\Http\Client\Request; + use Illuminate\Support\Facades\Http; + Http::fake(); Http::post('http://example.com/users', [ @@ -317,7 +325,7 @@ If needed, you may assert that a specific request was not sent using the `assert return $request->url() === 'http://example.com/posts'; }); -Or, if you would like to assert that no requests were sent, you may use the `assertNothingSent` method: +Or, you may use the `assertNothingSent` method to assert that no requests were sent during the test: Http::fake(); From 5c607d0cdb2f57a2fc8f055c39f08c10723ab464 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 12:05:27 -0600 Subject: [PATCH 115/274] wip --- localization.md | 79 ++++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/localization.md b/localization.md index 1e7ea1feb6..5ea69fd51c 100644 --- a/localization.md +++ b/localization.md @@ -13,7 +13,9 @@ <a name="introduction"></a> ## Introduction -Laravel's localization features provide a convenient way to retrieve strings in various languages, allowing you to easily support multiple languages within your application. Language 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: +Laravel's localization features provide a convenient way to retrieve strings in various languages, allowing you to easily support multiple languages within your application. + +Laravel provides two ways to manage translation strings. First, language strings may be stored in files within the `resources/lang` directory. Within this directory there may be subdirectories for each language supported by the application. This is the approach Laravel uses to manage translation strings for built-in Laravel features such as validation error messages: /resources /lang @@ -22,22 +24,25 @@ Laravel's localization features provide a convenient way to retrieve strings in /es messages.php -All language files return an array of keyed strings. For example: - - <?php +Or, translation strings may be defined within JSON files that are placed within the `resources/lang` directory. When taking this approach, each language supported by your application would have a corresponding JSON file within this directory. This approach is recommended for application's that have a large number of translatable strings: - return [ - 'welcome' => 'Welcome to our application', - ]; + /resources + /lang + en.json + es.json -> {note} For languages that differ by territory, you should name the language directories according to the ISO 15897. For example, "en_GB" should be used for British English rather than "en-gb". +We'll discuss each approach to managing translation strings within this documentation. <a name="configuring-the-locale"></a> ### Configuring The Locale -The default language for your application is stored in the `config/app.php` configuration file. You may modify this value to suit the needs of your application. You may also change the active language at runtime using the `setLocale` method on the `App` facade: +The default language for your application is stored in the `config/app.php` configuration file's `locale` configuration option. You are free to modify this value to suit the needs of your application. - Route::get('welcome/{locale}', function ($locale) { +You may modify the default language for a single HTTP request at runtime using the `setLocale` method provided by the `App` facade: + + use Illuminate\Support\Facades\App; + + Route::get('/greeting/{locale}', function ($locale) { if (! in_array($locale, ['en', 'es', 'fr'])) { abort(400); } @@ -54,9 +59,11 @@ You may configure a "fallback language", which will be used when the active lang <a name="determining-the-current-locale"></a> #### Determining The Current Locale -You may use the `getLocale` and `isLocale` methods on the `App` facade to determine the current locale or check if the locale is a given value: +You may use the `currentLocale` and `isLocale` methods on the `App` facade to determine the current locale or check if the locale is a given value: + + use Illuminate\Support\Facades\App; - $locale = App::getLocale(); + $locale = App::currentLocale(); if (App::isLocale('en')) { // @@ -68,7 +75,7 @@ You may use the `getLocale` and `isLocale` methods on the `App` facade to determ <a name="using-short-keys"></a> ### Using Short Keys -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: +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 your application. This is the approach Laravel uses to manage translation strings for built-in Laravel features such as validation error messages: /resources /lang @@ -84,19 +91,23 @@ All language files return an array of keyed strings. For example: // resources/lang/en/messages.php return [ - 'welcome' => 'Welcome to our application', + 'welcome' => 'Welcome to our application!', ]; +> {note} For languages that differ by territory, you should name the language directories according to the ISO 15897. For example, "en_GB" should be used for British English rather than "en-gb". + <a name="using-translation-strings-as-keys"></a> ### Using Translation Strings As Keys -For applications with heavy translation requirements, defining every string with a "short key" can become confusing when referencing the keys in your views. For this reason, Laravel also provides support for defining translation strings using the "default" translation of the string as the key. +For applications with a large number of translatable strings, defining every string with a "short key" can become confusing when referencing the keys in your views and it is cumbersome to continually invent keys for every translation string supported by your application. -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: +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 programar." - } +```js +{ + "I love programming.": "Me encanta programar." +} +``` #### Key / File Conflicts @@ -105,21 +116,21 @@ You should not define translation string keys that conflict with other translati <a name="retrieving-translation-strings"></a> ## 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: +You may retrieve translation strings from your language files using the `__` helper function. If you are using "short keys" to define your translation strings, you should pass the file that contains the key and the key itself to the `__` function using "dot" syntax. For example, let's retrieve the `welcome` translation string from the `resources/lang/en/messages.php` language file: echo __('messages.welcome'); - echo __('I love programming.'); +If the specified translation string does not exist, the `__` function will return the translation string key. So, using the example above, the `__` function would return `messages.welcome` if the translation string does not exist. -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: + If you are using your [default translation strings as your translation keys](#using-translation-strings-as-keys), you should pass the default translation of your string to the `__` function; - {{ __('messages.welcome') }} + echo __('I love programming.'); - @lang('messages.welcome') +Again, if the translation string does not exist, the `__` function will return the translation string key that it was given. -If the specified translation string does not exist, the `__` function will return the translation string key. So, using the example above, the `__` function would return `messages.welcome` if the translation string does not exist. +If you are using the [Blade templating engine](/docs/{{version}}/blade), you may use the `{{ }}` echo syntax to display the translation string: -> {note} The `@lang` directive does not escape any output. You are **fully responsible** for escaping your own output when using this directive. + {{ __('messages.welcome') }} <a name="replacing-parameters-in-translation-strings"></a> ### Replacing Parameters In Translation Strings @@ -128,7 +139,7 @@ If you wish, you may define placeholders in your translation strings. All placeh 'welcome' => 'Welcome, :name', -To replace the placeholders when retrieving a translation string, pass an array of replacements as the second argument to the `__` function: +To replace the placeholders when retrieving a translation string, you may pass an array of replacements as the second argument to the `__` function: echo __('messages.welcome', ['name' => 'dayle']); @@ -140,11 +151,19 @@ If your placeholder contains all capital letters, or only has its first letter c <a name="pluralization"></a> ### Pluralization -Pluralization is a complex problem, as different languages have a variety of complex rules for pluralization. By using a "pipe" character, you may distinguish singular and plural forms of a string: +Pluralization is a complex problem, as different languages have a variety of complex rules for pluralization; however, Laravel can help you translate strings differently based on pluralization rules that you define. Using a `|` character, you may distinguish singular and plural forms of a string: 'apples' => 'There is one apple|There are many apples', -You may even create more complex pluralization rules which specify translation strings for multiple number ranges: +Of course, pluralization is also supported when using [translation strings as keys](#using-translation-strings-as-keys): + +```js +{ + "There is one apple|There are many apples": "Hay una manzana|Hay muchas manzanas" +} +``` + +You may even create more complex pluralization rules which specify translation strings for multiple ranges of values: 'apples' => '{0} There are none|[1,19] There are some|[20,*] There are many', @@ -158,7 +177,7 @@ You may also define placeholder attributes in pluralization strings. These place echo trans_choice('time.minutes_ago', 5, ['value' => 5]); -If you would like to display the integer value that was passed to the `trans_choice` function, you may use the `:count` placeholder: +If you would like to display the integer value that was passed to the `trans_choice` function, you may use the built-in `:count` placeholder: 'apples' => '{0} There are none|{1} There is one|[2,*] There are :count', From eb33774fd1f4b2c6842ada27a29b4ac17d68a5d3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 14:09:06 -0600 Subject: [PATCH 116/274] wip --- mail.md | 130 +++++++++++++++++++++++++++----------------------------- 1 file changed, 62 insertions(+), 68 deletions(-) diff --git a/mail.md b/mail.md index feff45ee2f..6f1cc761ec 100644 --- a/mail.md +++ b/mail.md @@ -26,31 +26,33 @@ <a name="introduction"></a> ## Introduction -Laravel provides a clean, simple API over the popular [SwiftMailer](https://swiftmailer.symfony.com/) library with drivers for SMTP, Mailgun, Postmark, Amazon SES, and `sendmail`, allowing you to quickly get started sending mail through a local or cloud based service of your choice. +Sending email doesn't have to be complicated. Laravel provides a clean, simple email API powered by the popular [SwiftMailer](https://swiftmailer.symfony.com/) library. Laravel and SwiftMailer provide drivers for sending email via SMTP, Mailgun, Postmark, Amazon SES, and `sendmail`, allowing you to quickly get started sending mail through a local or cloud based service of your choice. <a name="configuration"></a> ### Configuration -Laravel's email services may be configured via the `mail` configuration file. Each mailer configured within this file may have its own options and even its own unique "transport", allowing your application to use different email services to send certain email messages. For example, your application might use Postmark to send transactional mail while using Amazon SES to send bulk mail. +Laravel's email services may be configured via your application's `config/mail.php` configuration file. Each mailer configured within this file may have its own unique configuration and even its own unique "transport", allowing your application to use different email services to send certain email messages. For example, your application might use Postmark to send transactional emails while using Amazon SES to send bulk emails. + +Within your `mail` configuration file, you will find a `mailers` configuration array. This array contains a sample configuration entry for each of the major mail drivers / transports supported by Laravel, while the `default` configuration value determines which mailer will be used by default when your application needs to send an email message. <a name="driver-prerequisites"></a> -### Driver Prerequisites +### Driver / Transport Prerequisites -The API based drivers such as Mailgun and Postmark are often simpler and faster than SMTP servers. If possible, you should use one of these drivers. All of the API drivers require the Guzzle HTTP library, which may be installed via the Composer package manager: +The API based drivers such as Mailgun and Postmark are often simpler and faster than sending mail via SMTP servers. Whenever possible, we recommend that you use one of these drivers. All of the API based drivers require the Guzzle HTTP library, which may be installed via the Composer package manager: composer require guzzlehttp/guzzle <a name="mailgun-driver"></a> #### Mailgun Driver -To use the Mailgun driver, first install Guzzle, then set the `default` option in your `config/mail.php` configuration file to `mailgun`. Next, verify that your `config/services.php` configuration file contains the following options: +To use the Mailgun driver, first install the Guzzle HTTP library. Then, set the `default` option in your `config/mail.php` configuration file to `mailgun`. Next, verify that your `config/services.php` configuration file contains the following options: 'mailgun' => [ 'domain' => env('MAILGUN_DOMAIN'), 'secret' => env('MAILGUN_SECRET'), ], -If you are not using the "US" [Mailgun region](https://documentation.mailgun.com/en/latest/api-intro.html#mailgun-regions), you may define your region's endpoint in the `services` configuration file: +If you are not using the United States [Mailgun region](https://documentation.mailgun.com/en/latest/api-intro.html#mailgun-regions), you may define your region's endpoint in the `services` configuration file: 'mailgun' => [ 'domain' => env('MAILGUN_DOMAIN'), @@ -65,7 +67,7 @@ To use the Postmark driver, install Postmark's SwiftMailer transport via Compose composer require wildbit/swiftmailer-postmark -Next, install Guzzle and set the `default` option in your `config/mail.php` configuration file to `postmark`. Finally, verify that your `config/services.php` configuration file contains the following options: +Next, install the Guzzle HTTP library and set the `default` option in your `config/mail.php` configuration file to `postmark`. Finally, verify that your `config/services.php` configuration file contains the following options: 'postmark' => [ 'token' => env('POSTMARK_TOKEN'), @@ -74,9 +76,11 @@ Next, install Guzzle and set the `default` option in your `config/mail.php` conf <a name="ses-driver"></a> #### SES Driver -To use the Amazon SES driver you must first install the Amazon AWS SDK for PHP. You may install this library by adding the following line to your `composer.json` file's `require` section and running the `composer update` command: +To use the Amazon SES driver you must first install the Amazon AWS SDK for PHP. You may install this library via the Composer package manager: - "aws/aws-sdk-php": "~3.0" +```bash +composer require aws/aws-sdk-php +``` Next, set the `default` option in your `config/mail.php` configuration file to `ses` and verify that your `config/services.php` configuration file contains the following options: @@ -86,7 +90,7 @@ Next, set the `default` option in your `config/mail.php` configuration file to ` 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], -If you need to include [additional options](https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-email-2010-12-01.html#sendrawemail) when executing the SES `SendRawEmail` request, you may define an `options` array within your `ses` configuration: +If you would like to define [additional options](https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-email-2010-12-01.html#sendrawemail) that Laravel should pass to the AWS SDK's `SendRawEmail` method when sending an email, you may define an `options` array within your `ses` configuration: 'ses' => [ 'key' => env('AWS_ACCESS_KEY_ID'), @@ -95,10 +99,7 @@ If you need to include [additional options](https://docs.aws.amazon.com/aws-sdk- 'options' => [ 'ConfigurationSetName' => 'MyConfigurationSet', 'Tags' => [ - [ - 'Name' => 'foo', - 'Value' => 'bar', - ], + ['Name' => 'foo', 'Value' => 'bar'], ], ], ], @@ -106,14 +107,14 @@ If you need to include [additional options](https://docs.aws.amazon.com/aws-sdk- <a name="generating-mailables"></a> ## Generating Mailables -In Laravel, each type of email sent by your application is represented as a "mailable" class. These classes are stored in the `app/Mail` directory. Don't worry if you don't see this directory in your application, since it will be generated for you when you create your first mailable class using the `make:mail` command: +When building Laravel applications, each type of email sent by your application is represented as a "mailable" class. These classes are stored in the `app/Mail` directory. Don't worry if you don't see this directory in your application, since it will be generated for you when you create your first mailable class using the `make:mail` Artisan command: php artisan make:mail OrderShipped <a name="writing-mailables"></a> ## Writing Mailables -All of a mailable class' configuration is done in the `build` method. Within this method, you may call various methods such as `from`, `subject`, `view`, and `attach` to configure the email's presentation and delivery. +Once you have generated a mailable class, open it up so we can explore its contents. First, note that all of a mailable class' configuration is done in the `build` method. Within this method, you may call various methods such as `from`, `subject`, `view`, and `attach` to configure the email's presentation and delivery. <a name="configuring-the-sender"></a> ### Configuring The Sender @@ -202,7 +203,7 @@ Typically, you will want to pass some data to your view that you can utilize whe /** * The order instance. * - * @var Order + * @var \App\Models\Order */ public $order; @@ -388,7 +389,7 @@ The `attachData` method may be used to attach a raw string of bytes as an attach <a name="inline-attachments"></a> ### Inline Attachments -Embedding inline images into your emails is typically cumbersome; however, Laravel provides a convenient way to attach images to your emails and retrieving the appropriate CID. To embed an inline image, use the `embed` method on the `$message` variable within your email template. Laravel automatically makes the `$message` variable available to all of your email templates, so you don't need to worry about passing it in manually: +Embedding inline images into your emails is typically cumbersome; however, Laravel provides a convenient way to attach images to your emails. To embed an inline image, use the `embed` method on the `$message` variable within your email template. Laravel automatically makes the `$message` variable available to all of your email templates, so you don't need to worry about passing it in manually: <body> Here is an image: @@ -396,23 +397,23 @@ Embedding inline images into your emails is typically cumbersome; however, Larav <img src="{{ $message->embed($pathToImage) }}"> </body> -> {note} `$message` variable is not available in plain-text messages since plain-text messages do not utilize inline attachments. +> {note} The `$message` variable is not available in plain-text message templates since plain-text messages do not utilize inline attachments. <a name="embedding-raw-data-attachments"></a> #### Embedding Raw Data Attachments -If you already have a raw data string you wish to embed into an email template, you may use the `embedData` method on the `$message` variable: +If you already have a raw image data string you wish to embed into an email template, you may call the `embedData` method on the `$message` variable. When calling the `embedData` method, you will need to provide a filename that should be assigned to the embedded image: <body> Here is an image from raw data: - <img src="{{ $message->embedData($data, $name) }}"> + <img src="{{ $message->embedData($data, 'example-image.jpg') }}"> </body> <a name="customizing-the-swiftmailer-message"></a> ### Customizing The SwiftMailer Message -The `withSwiftMessage` method of the `Mailable` base class allows you to register a callback which will be invoked with the raw SwiftMailer message instance before sending the message. This gives you an opportunity to customize the message before it is delivered: +The `withSwiftMessage` method of the `Mailable` base class allows you to register a closure which will be invoked with the SwiftMailer message instance before sending the message. This gives you an opportunity to deeply customize the message before it is delivered: /** * Build the message. @@ -424,15 +425,16 @@ The `withSwiftMessage` method of the `Mailable` base class allows you to registe $this->view('emails.orders.shipped'); $this->withSwiftMessage(function ($message) { - $message->getHeaders() - ->addTextHeader('Custom-Header', 'HeaderValue'); + $message->getHeaders()->addTextHeader( + 'Custom-Header', 'Header Value' + ); }); } <a name="markdown-mailables"></a> ## 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. +Markdown mailable messages allow you to take advantage of the pre-built templates and components of [mail notifications](/docs/{{version}}/notifications#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. <a name="generating-markdown-mailables"></a> ### Generating Markdown Mailables @@ -451,13 +453,15 @@ Then, when configuring the mailable within its `build` method, call the `markdow public function build() { return $this->from('example@example.com') - ->markdown('emails.orders.shipped'); + ->markdown('emails.orders.shipped', [ + 'url' => $this->orderUrl, + ]); } <a name="writing-markdown-messages"></a> ### 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: +Markdown mailables use a combination of Blade components and Markdown syntax which allow you to easily construct mail messages while leveraging Laravel's pre-built email UI components: @component('mail::message') # Order Shipped @@ -472,7 +476,7 @@ Markdown mailables use a combination of Blade components and Markdown syntax whi {{ config('app.name') }} @endcomponent -> {tip} Do not use excess indentation when writing Markdown emails. Markdown parsers will render indented content as code blocks. +> {tip} Do not use excess indentation when writing Markdown emails. Per Markdown standards, Markdown parsers will render indented content as code blocks. <a name="button-component"></a> #### Button Component @@ -516,16 +520,16 @@ This command will publish the Markdown mail components to the `resources/views/v <a name="customizing-the-css"></a> #### 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. +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 converted to inline CSS styles within the HTML representations of your Markdown mail messages. -If you would like to build an entirely new theme for Laravel's Markdown components, you may place a CSS file within the `html/themes` directory. After naming and saving your CSS file, update the `theme` option of the `mail` configuration file to match the name of your new theme. +If you would like to build an entirely new theme for Laravel's Markdown components, you may place a CSS file within the `html/themes` directory. After naming and saving your CSS file, update the `theme` option of your application's `config/mail.php` configuration file to match the name of your new theme. To customize the theme for an individual mailable, you may set the `$theme` property of the mailable class to the name of the theme that should be used when sending that mailable. <a name="sending-mail"></a> ## Sending Mail -To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/facades). The `to` method accepts an email address, a user instance, or a collection of users. If you pass an object or collection of objects, the mailer will automatically use their `email` and `name` properties when setting the email recipients, so make sure these attributes are available on your objects. Once you have specified your recipients, you may pass an instance of your mailable class to the `send` method: +To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/facades). The `to` method accepts an email address, a user instance, or a collection of users. If you pass an object or collection of objects, the mailer will automatically use their `email` and `name` properties when determining the email's recipients, so make sure these attributes are available on your objects. Once you have specified your recipients, you may pass an instance of your mailable class to the `send` method: <?php @@ -537,28 +541,25 @@ To send a message, use the `to` method on the `Mail` [facade](/docs/{{version}}/ use Illuminate\Http\Request; use Illuminate\Support\Facades\Mail; - class OrderController extends Controller + class OrderShipmentController extends Controller { /** * Ship the given order. * - * @param Request $request - * @param int $orderId - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ - public function ship(Request $request, $orderId) + public function store(Request $request) { - $order = Order::findOrFail($orderId); + $order = Order::findOrFail($request->order_id); - // Ship order... + // Ship the order... Mail::to($request->user())->send(new OrderShipped($order)); } } -You are not limited to just specifying the "to" recipients when sending a message. You are free to set "to", "cc", and "bcc" recipients all within a single, chained method call: - - use Illuminate\Support\Facades\Mail; +You are not limited to just specifying the "to" recipients when sending a message. You are free to set "to", "cc", and "bcc" recipients by chaining their respective methods together: Mail::to($request->user()) ->cc($moreUsers) @@ -568,7 +569,7 @@ You are not limited to just specifying the "to" recipients when sending a messag <a name="looping-over-recipients"></a> #### Looping Over Recipients -Occasionally, you may need to send a mailable to a list of recipients by iterating over an array of recipients / email addresses. Since the `to` method appends email addresses to the mailable's list of recipients, you should always re-create the mailable instance for each recipient: +Occasionally, you may need to send a mailable to a list of recipients by iterating over an array of recipients / email addresses. However, since the `to` method appends email addresses to the mailable's list of recipients, each iteration through the loop will send another email to every previous recipient. Therefore, you should always re-create the mailable instance for each recipient: foreach (['taylor@example.com', 'dries@example.com'] as $recipient) { Mail::to($recipient)->send(new OrderShipped($order)); @@ -577,7 +578,7 @@ Occasionally, you may need to send a mailable to a list of recipients by iterati <a name="sending-mail-via-a-specific-mailer"></a> #### Sending Mail Via A Specific Mailer -By default, Laravel will use the mailer configured as the `default` mailer in your `mail` configuration file. However, you may use the `mailer` method to send a message using a specific mailer configuration: +By default, Laravel will send email using the mailer configured as the `default` mailer in your application's `mail` configuration file. However, you may use the `mailer` method to send a message using a specific mailer configuration: Mail::mailer('postmark') ->to($request->user()) @@ -589,7 +590,7 @@ By default, Laravel will use the mailer configured as the `default` mailer in yo <a name="queueing-a-mail-message"></a> #### Queueing A Mail Message -Since sending email messages can drastically lengthen the response time of your application, many developers choose to queue email messages for background sending. Laravel makes this easy using its built-in [unified queue API](/docs/{{version}}/queues). To queue a mail message, use the `queue` method on the `Mail` facade after specifying the message's recipients: +Since sending email messages can negatively impact the response time of your application, many developers choose to queue email messages for background sending. Laravel makes this easy using its built-in [unified queue API](/docs/{{version}}/queues). To queue a mail message, use the `queue` method on the `Mail` facade after specifying the message's recipients: Mail::to($request->user()) ->cc($moreUsers) @@ -603,12 +604,10 @@ This method will automatically take care of pushing a job onto the queue so the If you wish to delay the delivery of a queued email message, you may use the `later` method. As its first argument, the `later` method accepts a `DateTime` instance indicating when the message should be sent: - $when = now()->addMinutes(10); - Mail::to($request->user()) ->cc($moreUsers) ->bcc($evenMoreUsers) - ->later($when, new OrderShipped($order)); + ->later(now()->addMinutes(10), new OrderShipped($order)); <a name="pushing-to-specific-queues"></a> #### Pushing To Specific Queues @@ -639,31 +638,34 @@ If you have mailable classes that you want to always be queued, you may implemen <a name="rendering-mailables"></a> ## Rendering Mailables -Sometimes you may wish to capture the HTML content of a mailable without sending it. To accomplish this, you may call the `render` method of the mailable. This method will return the evaluated contents of the mailable as a string: +Sometimes you may wish to capture the HTML content of a mailable without sending it. To accomplish this, you may call the `render` method of the mailable. This method will return the evaluated HTML content of the mailable as a string: - $invoice = App\Models\Invoice::find(1); + use App\Mail\InvoicePaid; + use App\Models\Invoice; - return (new App\Mail\InvoicePaid($invoice))->render(); + $invoice = Invoice::find(1); + + return (new InvoicePaid($invoice))->render(); <a name="previewing-mailables-in-the-browser"></a> ### Previewing Mailables In The Browser When designing a mailable's template, it is convenient to quickly preview the rendered mailable in your browser like a typical Blade template. For this reason, Laravel allows you to return any mailable directly from a route closure or controller. When a mailable is returned, it will be rendered and displayed in the browser, allowing you to quickly preview its design without needing to send it to an actual email address: - Route::get('mailable', function () { + Route::get('/mailable', function () { $invoice = App\Models\Invoice::find(1); return new App\Mail\InvoicePaid($invoice); }); -> {note} [Inline attachements](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [MailHog](https://github.com/mailhog/MailHog) or [HELO](https://usehelo.com). +> {note} [Inline attachments](#inline-attachments) will not be rendered when a mailable is previewed in your browser. To preview these mailables, you should send them to an email testing application such as [MailHog](https://github.com/mailhog/MailHog) or [HELO](https://usehelo.com). <a name="localizing-mailables"></a> ## Localizing Mailables -Laravel allows you to send mailables in a locale other than the current language, and will even remember this locale if the mail is queued. +Laravel allows you to send mailables in a locale other than the request's current locale, and will even remember this locale if the mail is queued. -To accomplish this, the `Mail` facade offers a `locale` method to set the desired language. The application will change into this locale when the mailable is being formatted and then revert back to the previous locale when formatting is complete: +To accomplish this, the `Mail` facade offers a `locale` method to set the desired language. The application will change into this locale when the mailable's template is being evaluated and then revert back to the previous locale when evaluation is complete: Mail::to($request->user())->locale('es')->send( new OrderShipped($order) @@ -701,27 +703,19 @@ When developing an application that sends email, you probably don't want to actu <a name="log-driver"></a> #### 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}}/configuration#environment-configuration). - -<a name="universal-to"></a> -#### Universal To - -Another solution provided by Laravel is to set a universal recipient of all emails sent by the framework. This way, all the emails generated by your application will be sent to a specific address, instead of the address actually specified when sending the message. This can be done via the `to` option in your `config/mail.php` configuration file: - - 'to' => [ - 'address' => 'example@example.com', - 'name' => 'Example' - ], +Instead of sending your emails, the `log` mail driver will write all email messages to your log files for inspection. Typically, this driver would only be used during local development. For more information on configuring your application per environment, check out the [configuration documentation](/docs/{{version}}/configuration#environment-configuration). <a name="mailtrap"></a> -#### Mailtrap +#### HELO / Mailtrap / MailHog + +Finally, you may use a service like [HELO](https://usehelo.com) or [Mailtrap](https://mailtrap.io) and the `smtp` driver to send your email messages to a "dummy" mailbox where you may view them in a true email client. This approach has the benefit of allowing you to actually inspect the final emails in Mailtrap's message viewer. -Finally, you may use a service like [Mailtrap](https://mailtrap.io) and the `smtp` driver to send your email messages to a "dummy" mailbox where you may view them in a true email client. This approach has the benefit of allowing you to actually inspect the final emails in Mailtrap's message viewer. +If you are using [Laravel Sail](/docs/{{version}}/installation#laravel-sail), you may preview your messages using [MailHog](https://github.com/mailhog/MailHog). When Sail is running, you may access the MailHog interface at: `http://localhost:8025`. <a name="events"></a> ## Events -Laravel fires two events during the process of sending mail messages. The `MessageSending` event is fired prior to a message being sent, while the `MessageSent` event is fired after a message has been sent. Remember, these events are fired when the mail is being *sent*, not when it is queued. You may register an event listener for this event in your `EventServiceProvider`: +Laravel fires two events during the process of sending mail messages. The `MessageSending` event is fired prior to a message being sent, while the `MessageSent` event is fired after a message has been sent. Remember, these events are fired when the mail is being *sent*, not when it is queued. You may register event listeners for this event in your `App\Providers\EventServiceProvider` service provider: /** * The event listener mappings for the application. From 2574aba3606ce07e302f912e35f0186a95e88d94 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 14:46:30 -0600 Subject: [PATCH 117/274] wip --- notifications.md | 97 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 31 deletions(-) diff --git a/notifications.md b/notifications.md index 4999a2c96e..e14778d1c6 100644 --- a/notifications.md +++ b/notifications.md @@ -48,18 +48,18 @@ <a name="introduction"></a> ## 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 [Vonage](https://www.vonage.com/communications-apis/), formerly known as Nexmo), and [Slack](https://slack.com). Notifications may also be stored in a database so they may be displayed in your web interface. +In addition to support for [sending email](/docs/{{version}}/mail), Laravel provides support for sending notifications across a variety of delivery channels, including email, SMS (via [Vonage](https://www.vonage.com/communications-apis/), formerly known as Nexmo), and [Slack](https://slack.com). In addition, a variety of [community built notification channels](https://laravel-notification-channels.com/about/#suggesting-a-new-channel) have been created to send notification over dozens of different channels! Notifications may also be stored in a database so they may be displayed in your web interface. Typically, notifications should be short, informational messages that notify users of something that occurred in your application. For example, if you are writing a billing application, you might send an "Invoice Paid" notification to your users via the email and SMS channels. <a name="creating-notifications"></a> ## Creating Notifications -In Laravel, each notification is represented by a single class (typically stored in the `app/Notifications` directory). Don't worry if you don't see this directory in your application, it will be created for you when you run the `make:notification` Artisan command: +In Laravel, each notification is represented by a single class that is typically stored in the `app/Notifications` directory. Don't worry if you don't see this directory in your application - it will be created for you when you run the `make:notification` Artisan command: php artisan make:notification InvoicePaid -This command will place a fresh notification class in your `app/Notifications` directory. Each notification class contains a `via` method and a variable number of message building methods (such as `toMail` or `toDatabase`) that convert the notification to a message optimized for that particular channel. +This command will place a fresh notification class in your `app/Notifications` directory. Each notification class contains a `via` method and a variable number of message building methods, such as `toMail` or `toDatabase`, that convert the notification to a message tailored for that particular channel. <a name="sending-notifications"></a> ## Sending Notifications @@ -67,7 +67,7 @@ This command will place a fresh notification class in your `app/Notifications` d <a name="using-the-notifiable-trait"></a> ### 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 explore using the trait: +Notifications may be sent in two ways: using the `notify` method of the `Notifiable` trait or using the `Notification` [facade](/docs/{{version}}/facades). The `Notifiable` trait is included on your application's `App\Models\User` model by default: <?php @@ -81,18 +81,20 @@ Notifications may be sent in two ways: using the `notify` method of the `Notifia use Notifiable; } -This trait is utilized by the default `App\Models\User` model and contains one method that may be used to send notifications: `notify`. The `notify` method expects to receive a notification instance: +The `notify` method that is provided by this trait expects to receive a notification instance: use App\Notifications\InvoicePaid; $user->notify(new InvoicePaid($invoice)); -> {tip} Remember, you may use the `Illuminate\Notifications\Notifiable` trait on any of your models. You are not limited to only including it on your `User` model. +> {tip} Remember, you may use the `Notifiable` trait on any of your models. You are not limited to only including it on your `User` model. <a name="using-the-notification-facade"></a> ### 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 approach is useful 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: + + use Illuminate\Support\Facades\Notification; Notification::send($users, new InvoicePaid($invoice)); @@ -121,7 +123,7 @@ The `via` method receives a `$notifiable` instance, which will be an instance of > {note} Before queueing notifications you should configure your queue and [start a worker](/docs/{{version}}/queues). -Sending notifications can take time, especially if the channel needs an external API call to deliver the notification. To speed up your application's response time, let your notification be queued by adding the `ShouldQueue` interface and `Queueable` trait to your class. The interface and trait are already imported for all notifications generated using `make:notification`, so you may immediately add them to your notification class: +Sending notifications can take time, especially if the channel needs to make an external API call to deliver the notification. To speed up your application's response time, let your notification be queued by adding the `ShouldQueue` interface and `Queueable` trait to your class. The interface and trait are already imported for all notifications generated using the `make:notification` command, so you may immediately add them to your notification class: <?php @@ -148,6 +150,18 @@ If you would like to delay the delivery of the notification, you may chain the ` $user->notify((new InvoicePaid($invoice))->delay($when)); +<a name="customizing-the-notification-queue-connection"></a> +#### Customizing The Notification Queue Connection + +By default, queued notifications will be queued using your application's default queue connection. If you would like to specify a different connection that should be used for a particular notification, you may define a `$connection` property on the notification class: + + /** + * The name of the queue connection to use when queueing the notification. + * + * @var string + */ + public $connection = 'redis'; + <a name="customizing-notification-channel-queues"></a> #### Customizing Notification Channel Queues @@ -169,7 +183,7 @@ If you would like to specify a specific queue that should be used for each notif <a name="on-demand-notifications"></a> ### On-Demand Notifications -Sometimes you may need to send a notification to someone who is not stored as a "user" of your application. Using the `Notification::route` facade method, you may specify ad-hoc notification routing information before sending the notification: +Sometimes you may need to send a notification to someone who is not stored as a "user" of your application. Using the `Notification` facade's `route` method, you may specify ad-hoc notification routing information before sending the notification: Notification::route('mail', 'taylor@example.com') ->route('nexmo', '5555555555') @@ -182,7 +196,9 @@ Sometimes you may need to send a notification to someone who is not stored as a <a name="formatting-mail-messages"></a> ### 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 an `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: +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 an `Illuminate\Notifications\Messages\MailMessage` instance. + +The `MailMessage` class contains a few simple methods to help you build transactional email messages. 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. @@ -203,14 +219,14 @@ If a notification supports being sent as an email, you should define a `toMail` > {tip} Note we are using `$this->invoice->id` in our `toMail` method. You may pass any data your notification needs to generate its message into the notification's constructor. -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: +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 beautiful, responsive HTML email template with a plain-text counterpart. Here is an example of an email generated by the `mail` channel: <img src="https://laravel.com/img/docs/notification-example-2.png"> -> {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. +> {tip} When sending mail notifications, be sure to set the `name` configuration option in your `config/app.php` configuration file. This value will be used in the header and footer of your mail notification messages. -<a name="other-notification-formatting-options"></a> -#### Other Notification Formatting Options +<a name="other-mail-notification-formatting-options"></a> +#### Other Mail Notification Formatting Options Instead of defining the "lines" of text in the notification class, you may use the `view` method to specify a custom template that should be used to render the notification email: @@ -227,7 +243,7 @@ Instead of defining the "lines" of text in the notification class, you may use t ); } -You may specify a plain-text view for the mail message by passing the view name as the second element of an array that is given to the `view` method of the `MailMessage`: +You may specify a plain-text view for the mail message by passing the view name as the second element of an array that is given to the `view` method: /** * Get the mail representation of the notification. @@ -245,7 +261,7 @@ You may specify a plain-text view for the mail message by passing the view name In addition, you may return a full [mailable object](/docs/{{version}}/mail) from the `toMail` method: - use App\Mail\InvoicePaid as Mailable; + use App\Mail\InvoicePaid as InvoicePaidMailable; /** * Get the mail representation of the notification. @@ -255,13 +271,14 @@ In addition, you may return a full [mailable object](/docs/{{version}}/mail) fro */ public function toMail($notifiable) { - return (new Mailable($this->invoice))->to($notifiable->email); + return (new InvoicePaidMailable($this->invoice)) + ->to($notifiable->email); } <a name="error-messages"></a> #### 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: +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 black: /** * Get the mail representation of the notification. @@ -291,14 +308,14 @@ By default, the email's sender / from address is defined in the `config/mail.php public function toMail($notifiable) { return (new MailMessage) - ->from('test@example.com', 'Example') + ->from('barrett@example.com', 'Barrett Blair') ->line('...'); } <a name="customizing-the-recipient"></a> ### Customizing The Recipient -When sending notifications via the `mail` channel, the notification system will automatically look for an `email` property on your notifiable entity. You may customize which email address is used to deliver the notification by defining a `routeNotificationForMail` method on the entity: +When sending notifications via the `mail` channel, the notification system will automatically look for an `email` property on your notifiable entity. You may customize which email address is used to deliver the notification by defining a `routeNotificationForMail` method on the notifiable entity: <?php @@ -322,7 +339,7 @@ When sending notifications via the `mail` channel, the notification system will // Return email address only... return $this->email_address; - // Return name and email address... + // Return email address and name... return [$this->email_address => $this->name]; } } @@ -330,7 +347,7 @@ When sending notifications via the `mail` channel, the notification system will <a name="customizing-the-subject"></a> ### Customizing The Subject -By default, the email's subject is the class name of the notification formatted to "title case". So, if your notification class is named `InvoicePaid`, the email's subject will be `Invoice Paid`. If you would like to specify an explicit subject for the message, you may call the `subject` method when building your message: +By default, the email's subject is the class name of the notification formatted to "Title Case". So, if your notification class is named `InvoicePaid`, the email's subject will be `Invoice Paid`. If you would like to specify an different subject for the message, you may call the `subject` method when building your message: /** * Get the mail representation of the notification. @@ -348,7 +365,7 @@ By default, the email's subject is the class name of the notification formatted <a name="customizing-the-mailer"></a> ### Customizing The Mailer -By default, the email notification will be sent using the default driver defined in the `config/mail.php` configuration file. However, you may specify a different mailer at runtime by calling the `mailer` method when building your message: +By default, the email notification will be sent using the default mailer defined in the `config/mail.php` configuration file. However, you may specify a different mailer at runtime by calling the `mailer` method when building your message: /** * Get the mail representation of the notification. @@ -373,7 +390,7 @@ You can modify the HTML and plain-text template used by mail notifications by pu <a name="mail-attachments"></a> ### Attachments -To add attachments to an email notification, use the `attach` method while building your message. The `attach` method accepts the full (absolute) path to the file as its first argument: +To add attachments to an email notification, use the `attach` method while building your message. The `attach` method accepts the absolute path to the file as its first argument: /** * Get the mail representation of the notification. @@ -406,12 +423,27 @@ When attaching files to a message, you may also specify the display name and / o ]); } -> {tip} Unlike attaching files in mailable objects, you may not attach a file directly from the storage disk using `attachFromStorage`. You should rather use the `attach` method with an absolute path to the file on the storage disk. Alternatively, you could return a [mailable](/docs/{{version}}/mail#generating-mailables) from the `toMail` method. +Unlike attaching files in mailable objects, you may not attach a file directly from a storage disk using `attachFromStorage`. You should rather use the `attach` method with an absolute path to the file on the storage disk. Alternatively, you could return a [mailable](/docs/{{version}}/mail#generating-mailables) from the `toMail` method: + + use App\Mail\InvoicePaid as InvoicePaidMailable; + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return Mailable + */ + public function toMail($notifiable) + { + return (new InvoicePaidMailable($this->invoice)) + ->to($notifiable->email) + ->attachFromStorage('/path/to/file'); + } <a name="raw-data-attachments"></a> #### Raw Data Attachments -The `attachData` method may be used to attach a raw string of bytes as an attachment: +The `attachData` method may be used to attach a raw string of bytes as an attachment. When calling the `attachData` method, you should provide the filename that should be assigned to the attachment: /** * Get the mail representation of the notification. @@ -433,10 +465,13 @@ The `attachData` method may be used to attach a raw string of bytes as an attach When designing a mail notification template, it is convenient to quickly preview the rendered mail message in your browser like a typical Blade template. For this reason, Laravel allows you to return any mail message generated by a mail notification directly from a route closure or controller. When a `MailMessage` is returned, it will be rendered and displayed in the browser, allowing you to quickly preview its design without needing to send it to an actual email address: - Route::get('mail', function () { - $invoice = App\Invoice::find(1); + use App\Invoice; + use App\Notifications\InvoicePaid; + + Route::get('/notification', function () { + $invoice = Invoice::find(1); - return (new App\Notifications\InvoicePaid($invoice)) + return (new InvoicePaid($invoice)) ->toMail($invoice->user); }); @@ -452,7 +487,7 @@ To generate a notification with a corresponding Markdown template, you may use t 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: +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. An array of data you wish to make available to the template may be passed as the method's second argument: /** * Get the mail representation of the notification. @@ -490,7 +525,7 @@ Markdown mail notifications use a combination of Blade components and Markdown s <a name="button-component"></a> #### 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: +The button component renders a centered button link. The component accepts two arguments, a `url` and an optional `color`. Supported colors are `primary`, `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 From a614b22008d6f9c3f5d2eab2e84c2395b3f3d45a Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 14:51:04 -0600 Subject: [PATCH 118/274] wip --- notifications.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/notifications.md b/notifications.md index e14778d1c6..22cdadf0be 100644 --- a/notifications.md +++ b/notifications.md @@ -590,9 +590,9 @@ To customize the theme for an individual notification, you may call the `theme` <a name="database-prerequisites"></a> ### Prerequisites -The `database` notification channel stores the notification information in a database table. This table will contain information such as the notification type as well as custom JSON data that describes the notification. +The `database` notification channel stores the notification information in a database table. This table will contain information such as the notification type as well as a JSON data structure that describes the notification. -You can query the table to display the notifications in your application's user interface. But, before you can do that, you will need to create a database table to hold your notifications. You may use the `notifications:table` command to generate a migration with the proper table schema: +You can query the table to display the notifications in your application's user interface. But, before you can do that, you will need to create a database table to hold your notifications. You may use the `notifications:table` command to generate a [migration](/docs/{{version}}/migrations) with the proper table schema: php artisan notifications:table @@ -620,12 +620,12 @@ If a notification supports being stored in a database table, you should define a <a name="todatabase-vs-toarray"></a> #### `toDatabase` Vs. `toArray` -The `toArray` method is also used by the `broadcast` channel to determine which data to broadcast to your JavaScript client. If you would like to have two different array representations for the `database` and `broadcast` channels, you should define a `toDatabase` method instead of a `toArray` method. +The `toArray` method is also used by the `broadcast` channel to determine which data to broadcast to your JavaScript powered frontend. If you would like to have two different array representations for the `database` and `broadcast` channels, you should define a `toDatabase` method instead of a `toArray` method. <a name="accessing-the-notifications"></a> ### Accessing The Notifications -Once notifications are stored in the database, you need a convenient way to access them from your notifiable entities. The `Illuminate\Notifications\Notifiable` trait, which is included on Laravel's default `App\Models\User` model, includes a `notifications` Eloquent relationship that returns the notifications for the entity. To fetch notifications, you may access this method like any other Eloquent relationship. By default, notifications will be sorted by the `created_at` timestamp: +Once notifications are stored in the database, you need a convenient way to access them from your notifiable entities. The `Illuminate\Notifications\Notifiable` trait, which is included on Laravel's default `App\Models\User` model, includes a `notifications` [Eloquent relationship](/docs/{{version}}/eloquent-relationships) that returns the notifications for the entity. To fetch notifications, you may access this method like any other Eloquent relationship. By default, notifications will be sorted by the `created_at` timestamp with the most recent notifications at the beginning of the collection: $user = App\Models\User::find(1); @@ -633,7 +633,7 @@ Once notifications are stored in the database, you need a convenient way to acce echo $notification->type; } -If you want to retrieve only the "unread" notifications, you may use the `unreadNotifications` relationship. Again, these notifications will be sorted by the `created_at` timestamp: +If you want to retrieve only the "unread" notifications, you may use the `unreadNotifications` relationship. Again, these notifications will be sorted by the `created_at` timestamp with the most recent notifications at the beginning of the collection: $user = App\Models\User::find(1); @@ -641,7 +641,7 @@ If you want to retrieve only the "unread" notifications, you may use the `unread echo $notification->type; } -> {tip} To access your notifications from your JavaScript client, you should define a notification controller for your application which returns the notifications for a notifiable entity, such as the current user. You may then make an HTTP request to that controller's URI from your JavaScript client. +> {tip} To access your notifications from your JavaScript client, you should define a notification controller for your application which returns the notifications for a notifiable entity, such as the current user. You may then make an HTTP request to that controller's URL from your JavaScript client. <a name="marking-notifications-as-read"></a> ### Marking Notifications As Read From 004307093acbd59722b7adcbae5720f3c50c6556 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 14:55:34 -0600 Subject: [PATCH 119/274] wip --- notifications.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/notifications.md b/notifications.md index 22cdadf0be..cccab4ea83 100644 --- a/notifications.md +++ b/notifications.md @@ -674,12 +674,12 @@ You may `delete` the notifications to remove them from the table entirely: <a name="broadcast-prerequisites"></a> ### Prerequisites -Before broadcasting notifications, you should configure and be familiar with Laravel's [event broadcasting](/docs/{{version}}/broadcasting) services. Event broadcasting provides a way to react to server-side fired Laravel events from your JavaScript client. +Before broadcasting notifications, you should configure and be familiar with Laravel's [event broadcasting](/docs/{{version}}/broadcasting) services. Event broadcasting provides a way to react to server-side Laravel events from your JavaScript powered frontend. <a name="formatting-broadcast-notifications"></a> ### 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 can define a `toBroadcast` method on the notification class. This method will receive a `$notifiable` entity and should return a `BroadcastMessage` instance. If the `toBroadcast` method does not exist, the `toArray` method will be used to gather the data that should be broadcast. The returned data will be encoded as JSON and broadcast to your JavaScript client. Let's take a look at an example `toBroadcast` method: +The `broadcast` channel broadcasts notifications using Laravel's [event broadcasting](/docs/{{version}}/broadcasting) services, allowing your JavaScript powered frontend to catch notifications in realtime. If a notification supports broadcasting, you can define a `toBroadcast` method on the notification class. This method will receive a `$notifiable` entity and should return a `BroadcastMessage` instance. If the `toBroadcast` method does not exist, the `toArray` method will be used to gather the data that should be broadcast. The returned data will be encoded as JSON and broadcast to your JavaScript powered frontend. Let's take a look at an example `toBroadcast` method: use Illuminate\Notifications\Messages\BroadcastMessage; @@ -709,7 +709,7 @@ All broadcast notifications are queued for broadcasting. If you would like to co <a name="customizing-the-notification-type"></a> #### Customizing The Notification Type -In addition to the data you specify, all broadcast notifications also have a `type` field containing the full class name of the notification. If you would like to customize the notification `type` that is provided to your JavaScript client, you may define a `broadcastType` method on the notification class: +In addition to the data you specify, all broadcast notifications also have a `type` field containing the full class name of the notification. If you would like to customize the notification `type`, you may define a `broadcastType` method on the notification class: use Illuminate\Notifications\Messages\BroadcastMessage; @@ -726,7 +726,7 @@ In addition to the data you specify, all broadcast notifications also have a `ty <a name="listening-for-notifications"></a> ### Listening For Notifications -Notifications will broadcast on a private channel formatted using a `{notifiable}.{id}` convention. So, if you are sending a notification to an `App\Models\User` instance with an ID of `1`, the notification will be broadcast on the `App.Models.User.1` private channel. When using [Laravel Echo](/docs/{{version}}/broadcasting), you may easily listen for notifications on a channel using the `notification` helper method: +Notifications will broadcast on a private channel formatted using a `{notifiable}.{id}` convention. So, if you are sending a notification to an `App\Models\User` instance with an ID of `1`, the notification will be broadcast on the `App.Models.User.1` private channel. When using [Laravel Echo](/docs/{{version}}/broadcasting), you may easily listen for notifications on a channel using the `notification` method: Echo.private('App.Models.User.' + userId) .notification((notification) => { @@ -736,7 +736,7 @@ Notifications will broadcast on a private channel formatted using a `{notifiable <a name="customizing-the-notification-channel"></a> #### 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: +If you would like to customize which channel that an entity's broadcast notifications are broadcast on, you may define a `receivesBroadcastNotificationsOn` method on the notifiable entity: <?php From 5b43f71601d5db6d19a42b2660a3db605e3c3cd6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 15:16:27 -0600 Subject: [PATCH 120/274] wip --- notifications.md | 96 +++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/notifications.md b/notifications.md index cccab4ea83..aebb0ffd13 100644 --- a/notifications.md +++ b/notifications.md @@ -767,19 +767,19 @@ If you would like to customize which channel that an entity's broadcast notifica <a name="sms-prerequisites"></a> ### Prerequisites -Sending SMS notifications in Laravel is powered by [Nexmo](https://www.nexmo.com/). Before you can send notifications via Nexmo, you need to install the `laravel/nexmo-notification-channel` Composer package: +Sending SMS notifications in Laravel is powered by [Vonage](https://www.vonage.com/) (formerly known as Nexmo). Before you can send notifications via Vonage, you need to install the `laravel/nexmo-notification-channel` and `nexmo/laravel` Composer packages - composer require laravel/nexmo-notification-channel + composer require laravel/nexmo-notification-channel nexmo/laravel -This will also install the [`nexmo/laravel`](https://github.com/Nexmo/nexmo-laravel) package. This package includes [its own configuration file](https://github.com/Nexmo/nexmo-laravel/blob/master/config/nexmo.php). You can use the `NEXMO_KEY` and `NEXMO_SECRET` environment variables to set your Nexmo public and secret key. +The `nexmo/laravel` package includes [its own configuration file](https://github.com/Nexmo/nexmo-laravel/blob/master/config/nexmo.php). However, you are not required to export this configuration file to your own application. You can simply use the `NEXMO_KEY` and `NEXMO_SECRET` environment variables to set your Vonage public and secret key. -Next, you will need to add a configuration option to your `config/services.php` configuration file. You may copy the example configuration below to get started: +Next, you will need to add a `nexmo` configuration entry to your `config/services.php` configuration file. You may copy the example configuration below to get started: 'nexmo' => [ 'sms_from' => '15556666666', ], -The `sms_from` option is the phone number that your SMS messages will be sent from. You should generate a phone number for your application in the Nexmo control panel. +The `sms_from` option is the phone number that your SMS messages will be sent from. You should generate a phone number for your application in the Vonage control panel. <a name="formatting-sms-notifications"></a> ### Formatting SMS Notifications @@ -787,10 +787,10 @@ The `sms_from` option is the phone number that your SMS messages will be sent fr If a notification supports being sent as an SMS, you should define a `toNexmo` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Messages\NexmoMessage` instance: /** - * Get the Nexmo / SMS representation of the notification. + * Get the Vonage / SMS representation of the notification. * * @param mixed $notifiable - * @return NexmoMessage + * @return \Illuminate\Notifications\Messages\NexmoMessage */ public function toNexmo($notifiable) { @@ -798,13 +798,31 @@ If a notification supports being sent as an SMS, you should define a `toNexmo` m ->content('Your SMS message content'); } +<a name="unicode-content"></a> +#### Unicode Content + +If your SMS message will contain unicode characters, you should call the `unicode` method when constructing the `NexmoMessage` instance: + + /** + * Get the Vonage / SMS representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\NexmoMessage + */ + public function toNexmo($notifiable) + { + return (new NexmoMessage) + ->content('Your unicode message') + ->unicode(); + } + <a name="formatting-shortcode-notifications"></a> ### Formatting Shortcode Notifications -Laravel also supports sending shortcode notifications, which are pre-defined message templates in your Nexmo account. You may specify the type of notification (`alert`, `2fa`, or `marketing`), as well as the custom values that will populate the template: +Laravel also supports sending shortcode notifications, which are pre-defined message templates in your Vonage account. To send a shortcode SMS notification, you should define a `toShortcode` method on your notification class. From within this method, you may return an array specifying the type of notification (`alert`, `2fa`, or `marketing`) as well as the custom values that will populate the template: /** - * Get the Nexmo / Shortcode representation of the notification. + * Get the Vonage / Shortcode representation of the notification. * * @param mixed $notifiable * @return array @@ -821,31 +839,13 @@ Laravel also supports sending shortcode notifications, which are pre-defined mes > {tip} Like [routing SMS Notifications](#routing-sms-notifications), you should implement the `routeNotificationForShortcode` method on your notifiable model. -<a name="unicode-content"></a> -#### 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(); - } - <a name="customizing-the-from-number"></a> ### Customizing The "From" Number -If you would like to send some notifications from a phone number that is different from the phone number specified in your `config/services.php` file, you may use the `from` method on a `NexmoMessage` instance: +If you would like to send some notifications from a phone number that is different from the phone number specified in your `config/services.php` file, you may call the `from` method on a `NexmoMessage` instance: /** - * Get the Nexmo / SMS representation of the notification. + * Get the Vonage / SMS representation of the notification. * * @param mixed $notifiable * @return NexmoMessage @@ -860,7 +860,7 @@ If you would like to send some notifications from a phone number that is differe <a name="routing-sms-notifications"></a> ### Routing SMS Notifications -To route Nexmo notifications to the proper phone number, define a `routeNotificationForNexmo` method on your notifiable entity: +To route Vonage notifications to the proper phone number, define a `routeNotificationForNexmo` method on your notifiable entity: <?php @@ -891,7 +891,7 @@ To route Nexmo notifications to the proper phone number, define a `routeNotifica <a name="slack-prerequisites"></a> ### Prerequisites -Before you can send notifications via Slack, you must install the notification channel via Composer: +Before you can send notifications via Slack, you must install the Slack notification channel via Composer: composer require laravel/slack-notification-channel @@ -906,7 +906,7 @@ If a notification supports being sent as a Slack message, you should define a `t * Get the Slack representation of the notification. * * @param mixed $notifiable - * @return SlackMessage + * @return \Illuminate\Notifications\Message\SlackMessage */ public function toSlack($notifiable) { @@ -923,23 +923,23 @@ You may use the `from` and `to` methods to customize the sender and recipient. T * Get the Slack representation of the notification. * * @param mixed $notifiable - * @return SlackMessage + * @return \Illuminate\Notifications\Messages\SlackMessage */ public function toSlack($notifiable) { return (new SlackMessage) ->from('Ghost', ':ghost:') - ->to('#other') - ->content('This will be sent to #other'); + ->to('#bots') + ->content('This will be sent to #bots'); } -You may also use an image as your logo instead of an emoji: +You may also use an image as your from "logo" instead of an emoji: /** * Get the Slack representation of the notification. * * @param mixed $notifiable - * @return SlackMessage + * @return \Illuminate\Notifications\Messages\SlackMessage */ public function toSlack($notifiable) { @@ -958,7 +958,7 @@ You may also add "attachments" to Slack messages. Attachments provide richer for * Get the Slack representation of the notification. * * @param mixed $notifiable - * @return SlackMessage + * @return \Illuminate\Notifications\Messages\SlackMessage */ public function toSlack($notifiable) { @@ -1027,7 +1027,7 @@ If some of your attachment fields contain Markdown, you may use the `markdown` m <a name="routing-slack-notifications"></a> ### Routing Slack Notifications -To route Slack notifications to the proper location, define a `routeNotificationForSlack` method on your notifiable entity. This should return the webhook URL to which the notification should be delivered. Webhook URLs may be generated by adding an "Incoming Webhook" service to your Slack team: +To route Slack notifications to the proper Slack team and channel, define a `routeNotificationForSlack` method on your notifiable entity. This should return the webhook URL to which the notification should be delivered. Webhook URLs may be generated by adding an "Incoming Webhook" service to your Slack team: <?php @@ -1055,15 +1055,17 @@ To route Slack notifications to the proper location, define a `routeNotification <a name="localizing-notifications"></a> ## Localizing Notifications -Laravel allows you to send notifications in a locale other than the current language, and will even remember this locale if the notification is queued. +Laravel allows you to send notifications in a locale other than the HTTP request's current locale, and will even remember this locale if the notification is queued. -To accomplish this, the `Illuminate\Notifications\Notification` class offers a `locale` method to set the desired language. The application will change into this locale when the notification is being formatted and then revert back to the previous locale when formatting is complete: +To accomplish this, the `Illuminate\Notifications\Notification` class offers a `locale` method to set the desired language. The application will change into this locale when the notification is being evaluated and then revert back to the previous locale when evaluation is complete: $user->notify((new InvoicePaid($invoice))->locale('es')); Localization of multiple notifiable entries may also be achieved via the `Notification` facade: - Notification::locale('es')->send($users, new InvoicePaid($invoice)); + Notification::locale('es')->send( + $users, new InvoicePaid($invoice) + ); <a name="user-preferred-locales"></a> ### User Preferred Locales @@ -1092,7 +1094,7 @@ Once you have implemented the interface, Laravel will automatically use the pref <a name="notification-events"></a> ## Notification Events -When a notification is sent, the `Illuminate\Notifications\Events\NotificationSent` event is fired by the notification system. This contains the "notifiable" entity and the notification instance itself. You may register listeners for this event in your `EventServiceProvider`: +When a notification is sent, the `Illuminate\Notifications\Events\NotificationSent` [event](/docs/{{version}}/events) is fired by the notification system. This contains the "notifiable" entity and the notification instance itself. You may register listeners for this event in your `EventServiceProvider`: /** * The event listener mappings for the application. @@ -1112,7 +1114,7 @@ Within an event listener, you may access the `notifiable`, `notification`, and ` /** * Handle the event. * - * @param NotificationSent $event + * @param \Illuminate\Notifications\Events\NotificationSent $event * @return void */ public function handle(NotificationSent $event) @@ -1126,7 +1128,9 @@ Within an event listener, you may access the `notifiable`, `notification`, and ` <a name="custom-channels"></a> ## Custom Channels -Laravel ships with a handful of notification channels, but you may want to write your own drivers to deliver notifications via other channels. Laravel makes it simple. To get started, define a class that contains a `send` method. The method should receive two arguments: a `$notifiable` and a `$notification`: +Laravel ships with a handful of notification channels, but you may want to write your own drivers to deliver notifications via other channels. Laravel makes it simple. To get started, define a class that contains a `send` method. The method should receive two arguments: a `$notifiable` and a `$notification`. + +Within the `send` method, you may call methods on the notification to retrieve a message object understood by your channel and then send the notification to the `$notifiable` instance however you wish: <?php @@ -1151,7 +1155,7 @@ Laravel ships with a handful of notification channels, but you may want to write } } -Once your notification channel class has been defined, you may return the class name from the `via` method of any of your notifications: +Once your notification channel class has been defined, you may return the class name from the `via` method of any of your notifications. In this example, the `toVoice` method of your notification can return whatever object you choose to represent voice messages. For example, you might define your own `VoiceMessage` class to represent these messages: <?php From 4f9f3fe1541dcac40bc4b26f4225d01d47d88c5d Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 15:38:43 -0600 Subject: [PATCH 121/274] wip --- packages.md | 84 +++++++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/packages.md b/packages.md index f72241e7ff..d65c2debde 100644 --- a/packages.md +++ b/packages.md @@ -18,16 +18,16 @@ <a name="introduction"></a> ## Introduction -Packages are the primary way of adding functionality to Laravel. Packages might be anything from a great way to work with dates like [Carbon](https://github.com/briannesbitt/Carbon), or an entire BDD testing framework like [Behat](https://github.com/Behat/Behat). +Packages are the primary way of adding functionality to Laravel. Packages might be anything from a great way to work with dates like [Carbon](https://github.com/briannesbitt/Carbon) or a package that allows you to associate files with Eloquent models like Spatie's [Laravel Media Library](https://github.com/spatie/laravel-medialibrary). -There are different types of packages. Some packages are stand-alone, meaning they work with any PHP framework. Carbon and Behat are examples of stand-alone packages. Any of these packages may be used with Laravel by requesting them in your `composer.json` file. +There are different types of packages. Some packages are stand-alone, meaning they work with any PHP framework. Carbon and PHPUnit are examples of stand-alone packages. Any of these packages may be used with Laravel by requiring them in your `composer.json` file. On the other hand, other packages are specifically intended for use with Laravel. These packages may have routes, controllers, views, and configuration specifically intended to enhance a Laravel application. This guide primarily covers the development of those packages that are Laravel specific. <a name="a-note-on-facades"></a> ### A Note On Facades -When writing a Laravel application, it generally does not matter if you use contracts or facades since both provide essentially equal levels of testability. However, when writing packages, your package will not typically have access to all of Laravel's testing helpers. If you would like to be able to write your package tests as if they existed inside a typical Laravel application, you may use the [Orchestral Testbench](https://github.com/orchestral/testbench) package. +When writing a Laravel application, it generally does not matter if you use contracts or facades since both provide essentially equal levels of testability. However, when writing packages, your package will not typically have access to all of Laravel's testing helpers. If you would like to be able to write your package tests as if the package were installed inside a typical Laravel application, you may use the [Orchestral Testbench](https://github.com/orchestral/testbench) package. <a name="package-discovery"></a> ## Package Discovery @@ -73,7 +73,7 @@ You may disable package discovery for all packages using the `*` character insid <a name="service-providers"></a> ## Service Providers -[Service providers](/docs/{{version}}/providers) are the connection points between your package and Laravel. A service provider is responsible for binding things into Laravel's [service container](/docs/{{version}}/container) and informing Laravel where to load package resources such as views, configuration, and localization files. +[Service providers](/docs/{{version}}/providers) are the connection point between your package and Laravel. A service provider is responsible for binding things into Laravel's [service container](/docs/{{version}}/container) and informing Laravel where to load package resources such as views, configuration, and localization files. A service provider extends the `Illuminate\Support\ServiceProvider` class and contains two methods: `register` and `boot`. The base `ServiceProvider` class is located in the `illuminate/support` Composer package, which you should add to your own package's dependencies. To learn more about the structure and purpose of service providers, check out [their documentation](/docs/{{version}}/providers). @@ -83,17 +83,17 @@ A service provider extends the `Illuminate\Support\ServiceProvider` class and co <a name="configuration"></a> ### Configuration -Typically, you will need to publish your package's configuration file to the application's own `config` directory. This will allow users of your package to easily override your default configuration options. To allow your configuration files to be published, call the `publishes` method from the `boot` method of your service provider: +Typically, you will need to publish your package's configuration file to the application's `config` directory. This will allow users of your package to easily override your default configuration options. To allow your configuration files to be published, call the `publishes` method from the `boot` method of your service provider: /** - * Bootstrap any application services. + * Bootstrap any package services. * * @return void */ public function boot() { $this->publishes([ - __DIR__.'/path/to/config/courier.php' => config_path('courier.php'), + __DIR__.'/../config/courier.php' => config_path('courier.php'), ]); } @@ -106,7 +106,9 @@ Now, when users of your package execute Laravel's `vendor:publish` command, your <a name="default-package-configuration"></a> #### 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: +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 file. To merge the configuration file values, use the `mergeConfigFrom` method within your service provider's `register` method. + +The `mergeConfigFrom` method accepts the path to your package's configuration file as its first argument and the name of the application's copy of the configuration file as its second argument: /** * Register any application services. @@ -116,7 +118,7 @@ You may also merge your own package configuration file with the application's pu public function register() { $this->mergeConfigFrom( - __DIR__.'/path/to/config/courier.php', 'courier' + __DIR__.'/../config/courier.php', 'courier' ); } @@ -128,13 +130,13 @@ You may also merge your own package configuration file with the application's pu 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: /** - * Bootstrap any application services. + * Bootstrap any package services. * * @return void */ public function boot() { - $this->loadRoutesFrom(__DIR__.'/routes.php'); + $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); } <a name="migrations"></a> @@ -143,16 +145,16 @@ If your package contains routes, you may load them using the `loadRoutesFrom` me If your package contains [database migrations](/docs/{{version}}/migrations), you may use the `loadMigrationsFrom` method to inform Laravel how to load them. The `loadMigrationsFrom` method accepts the path to your package's migrations as its only argument: /** - * Bootstrap any application services. + * Bootstrap any package services. * * @return void */ public function boot() { - $this->loadMigrationsFrom(__DIR__.'/path/to/migrations'); + $this->loadMigrationsFrom(__DIR__.'/../database/migrations'); } -Once your package's migrations have been registered, they will automatically be run when the `php artisan migrate` command is executed. You do not need to export them to the application's main `database/migrations` directory. +Once your package's migrations have been registered, they will automatically be run when the `php artisan migrate` command is executed. You do not need to export them to the application's `database/migrations` directory. <a name="translations"></a> ### Translations @@ -160,13 +162,13 @@ Once your package's migrations have been registered, they will automatically be If your package contains [translation files](/docs/{{version}}/localization), you may use the `loadTranslationsFrom` method to inform Laravel how to load them. For example, if your package is named `courier`, you should add the following to your service provider's `boot` method: /** - * Bootstrap any application services. + * Bootstrap any package services. * * @return void */ public function boot() { - $this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier'); + $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'courier'); } Package translations are referenced using the `package::file.line` syntax convention. So, you may load the `courier` package's `welcome` line from the `messages` file like so: @@ -179,16 +181,16 @@ Package translations are referenced using the `package::file.line` syntax conven If you would like to publish your package's translations to the application's `resources/lang/vendor` directory, you may use the service provider's `publishes` method. The `publishes` method accepts an array of package paths and their desired publish locations. For example, to publish the translation files for the `courier` package, you may do the following: /** - * Bootstrap any application services. + * Bootstrap any package services. * * @return void */ public function boot() { - $this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier'); + $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'courier'); $this->publishes([ - __DIR__.'/path/to/translations' => resource_path('lang/vendor/courier'), + __DIR__.'/../resources/lang' => resource_path('lang/vendor/courier'), ]); } @@ -200,25 +202,25 @@ Now, when users of your package execute Laravel's `vendor:publish` Artisan comma To register your package's [views](/docs/{{version}}/views) with Laravel, you need to tell Laravel where the views are located. You may do this using the service provider's `loadViewsFrom` method. The `loadViewsFrom` method accepts two arguments: the path to your view templates and your package's name. For example, if your package's name is `courier`, you would add the following to your service provider's `boot` method: /** - * Bootstrap any application services. + * Bootstrap any package services. * * @return void */ public function boot() { - $this->loadViewsFrom(__DIR__.'/path/to/views', 'courier'); + $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); } Package views are referenced using the `package::view` syntax convention. So, once your view path is registered in a service provider, you may load the `admin` view from the `courier` package like so: - Route::get('admin', function () { - return view('courier::admin'); + Route::get('/dashboard', function () { + return view('courier::dashboard'); }); <a name="overriding-package-views"></a> #### Overriding Package Views -When you use the `loadViewsFrom` method, Laravel actually registers two locations for your views: the application's `resources/views/vendor` directory and the directory you specify. So, using the `courier` example, Laravel will first check if a custom version of the view has been provided by the developer in `resources/views/vendor/courier`. Then, if the view has not been customized, Laravel will search the package view directory you specified in your call to `loadViewsFrom`. This makes it easy for package users to customize / override your package's views. +When you use the `loadViewsFrom` method, Laravel actually registers two locations for your views: the application's `resources/views/vendor` directory and the directory you specify. So, using the `courier` package as an example, Laravel will first check if a custom version of the view has been placed in the `resources/views/vendor/courier` directory by the developer. Then, if the view has not been customized, Laravel will search the package view directory you specified in your call to `loadViewsFrom`. This makes it easy for package users to customize / override your package's views. <a name="publishing-views"></a> #### Publishing Views @@ -226,16 +228,16 @@ When you use the `loadViewsFrom` method, Laravel actually registers two location If you would like to make your views available for publishing to the application's `resources/views/vendor` directory, you may use the service provider's `publishes` method. The `publishes` method accepts an array of package view paths and their desired publish locations: /** - * Bootstrap any application services. + * Bootstrap the package services. * * @return void */ public function boot() { - $this->loadViewsFrom(__DIR__.'/path/to/views', 'courier'); + $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); $this->publishes([ - __DIR__.'/path/to/views' => resource_path('views/vendor/courier'), + __DIR__.'/../resources/views' => resource_path('views/vendor/courier'), ]); } @@ -244,10 +246,13 @@ Now, when users of your package execute Laravel's `vendor:publish` Artisan comma <a name="view-components"></a> ### View Components -If your package contains [view components](/docs/{{version}}/blade#components), you may use the `loadViewComponentsAs` method to inform Laravel how to load them. The `loadViewComponentsAs` method accepts two arguments: the tag prefix for your view components and an array of your view components class. For example, if your package's prefix is `courier` and you have `Alert` and `Button` view components, you would add the following to your service provider's `boot` method: +If your package contains [view components](/docs/{{version}}/blade#components), you may use the `loadViewComponentsAs` method to inform Laravel how to load them. The `loadViewComponentsAs` method accepts two arguments: the tag prefix for your view components and an array of your view component class names. For example, if your package's prefix is `courier` and you have `Alert` and `Button` view components, you would add the following to your service provider's `boot` method: + + use Courier\Components\Alert; + use Courier\Components\Button; /** - * Bootstrap any application services. + * Bootstrap any package services. * * @return void */ @@ -277,8 +282,11 @@ If your package contains anonymous components, they must be placed within a `com 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): + use Courier\Console\Commands\InstallCommand; + use Courier\Console\Commands\NetworkCommand; + /** - * Bootstrap the application services. + * Bootstrap any package services. * * @return void */ @@ -286,8 +294,8 @@ To register your package's Artisan commands with Laravel, you may use the `comma { if ($this->app->runningInConsole()) { $this->commands([ - FooCommand::class, - BarCommand::class, + InstallCommand::class, + NetworkCommand::class, ]); } } @@ -295,31 +303,31 @@ To register your package's Artisan commands with Laravel, you may use the `comma <a name="public-assets"></a> ## Public Assets -Your package may have assets such as JavaScript, CSS, and images. To publish these assets to the application's `public` directory, use the service provider's `publishes` method. In this example, we will also add a `public` asset group tag, which may be used to publish groups of related assets: +Your package may have assets such as JavaScript, CSS, and images. To publish these assets to the application's `public` directory, use the service provider's `publishes` method. In this example, we will also add a `public` asset group tag, which may be used to easily publish groups of related assets: /** - * Bootstrap any application services. + * Bootstrap any package services. * * @return void */ public function boot() { $this->publishes([ - __DIR__.'/path/to/assets' => public_path('vendor/courier'), + __DIR__.'/../public' => public_path('vendor/courier'), ], 'public'); } -Now, when your package's users execute the `vendor:publish` command, your assets will be copied to the specified publish location. Since you will typically need to overwrite the assets every time the package is updated, you may use the `--force` flag: +Now, when your package's users execute the `vendor:publish` command, your assets will be copied to the specified publish location. Since users will typically need to overwrite the assets every time the package is updated, you may use the `--force` flag: php artisan vendor:publish --tag=public --force <a name="publishing-file-groups"></a> ## Publishing File Groups -You may want to publish groups of package assets and resources separately. For instance, you might want to allow your users to publish your package's configuration files without being forced to publish your package's assets. You may do this by "tagging" them when calling the `publishes` method from a package's service provider. For example, let's use tags to define two publish groups in the `boot` method of a package service provider: +You may want to publish groups of package assets and resources separately. For instance, you might want to allow your users to publish your package's configuration files without being forced to publish your package's assets. You may do this by "tagging" them when calling the `publishes` method from a package's service provider. For example, let's use tags to define two publish groups (`config` and `migrations`) in the `boot` method of a package's service provider: /** - * Bootstrap any application services. + * Bootstrap any package services. * * @return void */ From af2158042bfd6994b473a2cbedc45cb548a2aafb Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 15:57:07 -0600 Subject: [PATCH 122/274] wip --- queues.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/queues.md b/queues.md index 41189beee9..c2790987f3 100644 --- a/queues.md +++ b/queues.md @@ -41,24 +41,28 @@ <a name="introduction"></a> ## Introduction -> {tip} Laravel now offers Horizon, a beautiful dashboard and configuration system for your Redis powered queues. Check out the full [Horizon documentation](/docs/{{version}}/horizon) for more information. +While building your web application, you may have some tasks, such as parsing and storing an uploaded CSV file, that take too long to perform during a typical web request. Thankfully, Laravel allows you to easily create queued jobs that may be processed in the background. By moving time intensive tasks to a queue, your application can respond to web requests with blazing speed and provide a better user experience to your customers. + +Laravel queues provide a unified queueing API across a variety of different queue backends, such as [Amazon SQS](https://aws.amazon.com/sqs/), [Redis](https://redis.io), or even a relational database. -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. +Laravel's queue configuration options are stored in your application's `config/queue.php` configuration file. In this file you will find connection configurations for each of the queue drivers that are included with the framework, including the database, [Amazon SQS](https://aws.amazon.com/sqs/), [Redis](https://redis.io), and [Beanstalkd](https://beanstalkd.github.io/) drivers, as well as a synchronous driver that will execute jobs immediately (for use during local development). A `null` queue driver is also included which 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://beanstalkd.github.io/), [Amazon SQS](https://aws.amazon.com/sqs/), [Redis](https://redis.io), and a synchronous driver that will execute jobs immediately (for local use). A `null` queue driver is also included which discards queued jobs. +> {tip} Laravel now offers Horizon, a beautiful dashboard and configuration system for your Redis powered queues. Check out the full [Horizon documentation](/docs/{{version}}/horizon) for more information. <a name="connections-vs-queues"></a> ### Connections Vs. Queues -Before getting started with Laravel queues, it is important to understand the distinction between "connections" and "queues". In your `config/queue.php` configuration file, there is a `connections` configuration option. This option defines a particular connection to a backend service such as Amazon SQS, Beanstalk, or Redis. However, any given queue connection may have multiple "queues" which may be thought of as different stacks or piles of queued jobs. +Before getting started with Laravel queues, it is important to understand the distinction between "connections" and "queues". In your `config/queue.php` configuration file, there is a `connections` configuration array. This option defines the connections to backend queue services such as Amazon SQS, Beanstalk, or Redis. However, any given queue connection may have multiple "queues" which may be thought of as different stacks or piles of queued jobs. Note that each connection configuration example in the `queue` configuration file contains a `queue` attribute. This is the default queue that jobs will be dispatched to when they are sent to a given connection. In other words, if you dispatch a job without explicitly defining which queue it should be dispatched to, the job will be placed on the queue that is defined in the `queue` attribute of the connection configuration: - // This job is sent to the default queue... - Job::dispatch(); + use App\Jobs\ProcessPodcast; + + // This job is sent to the default connection's default queue... + ProcessPodcast::dispatch(); - // This job is sent to the "emails" queue... - Job::dispatch()->onQueue('emails'); + // This job is sent to the default connection's "emails" queue... + ProcessPodcast::dispatch()->onQueue('emails'); 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: @@ -111,7 +115,7 @@ Adjusting this value based on your queue load can be more efficient than continu <a name="other-driver-prerequisites"></a> #### Other Driver Prerequisites -The following dependencies are needed for the listed queue drivers: +The following dependencies are needed for the listed queue drivers. These dependencies may be installed via the Composer package manager: <div class="content-list" markdown="1"> - Amazon SQS: `aws/aws-sdk-php ~3.0` From 5d1271145bf8f184cee2a8223b4f0c6f917d50c9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 16:06:36 -0600 Subject: [PATCH 123/274] wip --- queues.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/queues.md b/queues.md index c2790987f3..0c7cf21ad8 100644 --- a/queues.md +++ b/queues.md @@ -129,7 +129,7 @@ The following dependencies are needed for the listed queue drivers. These depend <a name="generating-job-classes"></a> ### Generating Job Classes -By default, all of the queueable jobs for your application are stored in the `app/Jobs` directory. If the `app/Jobs` directory doesn't exist, it will be created when you run the `make:job` Artisan command. You may generate a new queued job using the Artisan CLI: +By default, all of the queueable jobs for your application are stored in the `app/Jobs` directory. If the `app/Jobs` directory doesn't exist, it will be created when you run the `make:job` Artisan command: php artisan make:job ProcessPodcast @@ -140,7 +140,7 @@ The generated class will implement the `Illuminate\Contracts\Queue\ShouldQueue` <a name="class-structure"></a> ### Class Structure -Job classes are very simple, normally containing only a `handle` method which is called when the job is processed by the queue. To get started, let's take a look at an example job class. In this example, we'll pretend we manage a podcast publishing service and need to process the uploaded podcast files before they are published: +Job classes are very simple, normally containing only a `handle` method that is invoked when the job is processed by the queue. To get started, let's take a look at an example job class. In this example, we'll pretend we manage a podcast publishing service and need to process the uploaded podcast files before they are published: <?php @@ -158,12 +158,17 @@ Job classes are very simple, normally containing only a `handle` method which is { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + /** + * The podcast instance. + * + * @var \App\Models\Podcast + */ protected $podcast; /** * Create a new job instance. * - * @param Podcast $podcast + * @param App\Models\Podcast $podcast * @return void */ public function __construct(Podcast $podcast) @@ -174,7 +179,7 @@ Job classes are very simple, normally containing only a `handle` method which is /** * Execute the job. * - * @param AudioProcessor $processor + * @param App\Services\AudioProcessor $processor * @return void */ public function handle(AudioProcessor $processor) @@ -183,15 +188,21 @@ Job classes are very simple, normally containing only a `handle` method which is } } -In this example, note that we were able to pass an [Eloquent model](/docs/{{version}}/eloquent) directly into the queued job's constructor. Because of the `SerializesModels` trait that the job is using, Eloquent models and their loaded relationships will be gracefully serialized and unserialized when the job is processing. If your queued job accepts an Eloquent model in its constructor, only the identifier for the model will be serialized onto the queue. When the job is actually handled, the queue system will automatically re-retrieve the full model instance and its loaded relationships from the database. It's all totally transparent to your application and prevents issues that can arise from serializing full Eloquent model instances. +In this example, note that we were able to pass an [Eloquent model](/docs/{{version}}/eloquent) directly into the queued job's constructor. Because of the `SerializesModels` trait that the job is using, Eloquent models and their loaded relationships will be gracefully serialized and unserialized when the job is processing. -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. +If your queued job accepts an Eloquent model in its constructor, only the identifier for the model will be serialized onto the queue. When the job is actually handled, the queue system will automatically re-retrieve the full model instance and its loaded relationships from the database. This approach to model serialization allows for much smaller job payloads to be sent to your queue driver. -If you would like to take total control over how the container injects dependencies into the `handle` method, you may use the container's `bindMethod` method. The `bindMethod` method accepts a callback which receives the job and the container. Within the callback, you are free to invoke the `handle` method however you wish. Typically, you should call this method from a [service provider](/docs/{{version}}/providers): +<a name="handle-method-dependency-injection"></a> +#### `handle` Method Dependency Injection + +The `handle` method is invoked 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. + +If you would like to take total control over how the container injects dependencies into the `handle` method, you may use the container's `bindMethod` method. The `bindMethod` method accepts a callback which receives the job and the container. Within the callback, you are free to invoke the `handle` method however you wish. Typically, you should call this method from the `boot` method of your `App\Providers\AppServiceProvider` [service provider](/docs/{{version}}/providers): use App\Jobs\ProcessPodcast; + use App\Services\AudioProcessor; - $this->app->bindMethod(ProcessPodcast::class.'@handle', function ($job, $app) { + $this->app->bindMethod([ProcessPodcast::class, 'handle'], function ($job, $app) { return $job->handle($app->make(AudioProcessor::class)); }); @@ -200,7 +211,7 @@ If you would like to take total control over how the container injects dependenc <a name="handling-relationships"></a> #### Handling Relationships -Because loaded relationships also get serialized, the serialized job string can become quite large. To prevent relations from being serialized, you can call the `withoutRelations` method on the model when setting a property value. This method will return an instance of the model with no loaded relationships: +Because loaded relationships also get serialized, the serialized job string can sometimes become quite large. To prevent relations from being serialized, you can call the `withoutRelations` method on the model when setting a property value. This method will return an instance of the model without its loaded relationships: /** * Create a new job instance. @@ -216,7 +227,7 @@ Because loaded relationships also get serialized, the serialized job string can <a name="unique-jobs"></a> ### Unique Jobs -> {note} Unique jobs require a cache driver that supports [locks](/docs/{{version}}/cache#atomic-locks). +> {note} Unique jobs require a cache driver that supports [locks](/docs/{{version}}/cache#atomic-locks). Currently, the `memcached`, `redis`, `dynamodb`, `database`, `file`, and `array` cache drivers support atomic locks. Sometimes, you may want to ensure that only one instance of a specific job is on the queue at any point in time. You may do so by implementing the `ShouldBeUnique` interface on your job class: From 9744aec1d3d471980c20301970a3e448bde8e421 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 16:20:48 -0600 Subject: [PATCH 124/274] wip --- queues.md | 54 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/queues.md b/queues.md index 0c7cf21ad8..ef1b96ab8b 100644 --- a/queues.md +++ b/queues.md @@ -229,7 +229,7 @@ Because loaded relationships also get serialized, the serialized job string can > {note} Unique jobs require a cache driver that supports [locks](/docs/{{version}}/cache#atomic-locks). Currently, the `memcached`, `redis`, `dynamodb`, `database`, `file`, and `array` cache drivers support atomic locks. -Sometimes, you may want to ensure that only one instance of a specific job is on the queue at any point in time. You may do so by implementing the `ShouldBeUnique` interface on your job class: +Sometimes, you may want to ensure that only one instance of a specific job is on the queue at any point in time. You may do so by implementing the `ShouldBeUnique` interface on your job class. This interface does not require you to define any additional methods on your class: <?php @@ -241,7 +241,7 @@ Sometimes, you may want to ensure that only one instance of a specific job is on ... } -In the example above, the `UpdateSearchIndex` job is unique. So, new dispatches of the job will be ignored if another instance of the job is already on the queue and has not finished processing. +In the example above, the `UpdateSearchIndex` job is unique. So, the job will not be dispatched if another instance of the job is already on the queue and has not finished processing. In certain cases, you may want to define a specific "key" that makes the job unique or you may want to specify a timeout beyond which the job no longer stays unique. To accomplish this, you may define `uniqueId` and `uniqueFor` properties or methods on your job class: @@ -283,7 +283,7 @@ In the example above, the `UpdateSearchIndex` job is unique by a product ID. So, <a name="unique-job-locks"></a> #### Unique Job Locks -Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts to acquire a [lock](/docs/{{version}}/cache#atomic-locks) with the `uniqueId` key. If the lock is not acquired, the dispatch is ignored. This lock is released when the job completes processing or fails all of its retry attempts. By default, Laravel will use the default cache driver to obtain this lock. However, if you wish to use another driver for acquiring the lock, you may define a `uniqueVia` method the returns the cache driver that should be used: +Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts to acquire a [lock](/docs/{{version}}/cache#atomic-locks) with the `uniqueId` key. If the lock is not acquired, the job is not dispatched. This lock is released when the job completes processing or fails all of its retry attempts. By default, Laravel will use the default cache driver to obtain this lock. However, if you wish to use another driver for acquiring the lock, you may define a `uniqueVia` method the returns the cache driver that should be used: use Illuminate\Support\Facades\Cache; @@ -309,6 +309,8 @@ Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts t Job middleware allow you to wrap custom logic around the execution of queued jobs, reducing boilerplate in the jobs themselves. For example, consider the following `handle` method which leverages Laravel's Redis rate limiting features to allow only one job to process every five seconds: + use Illuminate\Support\Facades\Redis; + /** * Execute the job. * @@ -327,7 +329,7 @@ Job middleware allow you to wrap custom logic around the execution of queued job }); } -While this code is valid, the structure of the `handle` method becomes noisy since it is cluttered with Redis rate limiting logic. In addition, this rate limiting logic must be duplicated for any other jobs that we want to rate limit. +While this code is valid, the implementation of the `handle` method becomes noisy since it is cluttered with Redis rate limiting logic. In addition, this rate limiting logic must be duplicated for any other jobs that we want to rate limit. Instead of rate limiting in the handle method, we could define a job middleware that handles rate limiting. Laravel does not have a default location for job middleware, so you are welcome to place job middleware anywhere in your application. In this example, we will place the middleware in a `app/Jobs/Middleware` directory: @@ -364,7 +366,7 @@ Instead of rate limiting in the handle method, we could define a job middleware As you can see, like [route middleware](/docs/{{version}}/middleware), job middleware receive the job being processed and a callback that should be invoked to continue processing the job. -After creating job middleware, they may be attached to a job by returning them from the job's `middleware` method. This method does not exist on jobs scaffolded by the `make:job` Artisan command, so you will need to add it to your own job class definition: +After creating job middleware, they may be attached to a job by returning them from the job's `middleware` method. This method does not exist on jobs scaffolded by the `make:job` Artisan command, so you will need to manually add it to your job class: use App\Jobs\Middleware\RateLimited; @@ -381,19 +383,31 @@ After creating job middleware, they may be attached to a job by returning them f <a name="rate-limiting"></a> ### Rate Limiting -Although we just demonstrated how to write your own rate limiting job middleware, Laravel includes a rate limiting middleware that you may utilize to rate limit jobs. Like [route rate limiters](/docs/{{version}}/routing#defining-rate-limiter), job rate limiters are defined using the `RateLimiter` facade's `for` method. +Although we just demonstrated how to write your own rate limiting job middleware, Laravel actually includes a rate limiting middleware that you may utilize to rate limit jobs. Like [route rate limiters](/docs/{{version}}/routing#defining-rate-limiter), job rate limiters are defined using the `RateLimiter` facade's `for` method. -For example, you may wish to allow users to backup their data once per hour while imposing no such limit on premium customers. To accomplish this, you may define a `RateLimiter` in your `AppServiceProvider`: +For example, you may wish to allow users to backup their data once per hour while imposing no such limit on premium customers. To accomplish this, you may define a `RateLimiter` in the `boot` method of your `AppServiceProvider`: use Illuminate\Support\Facades\RateLimiter; - RateLimiter::for('backups', function ($job) { - return $job->user->vipCustomer() - ? Limit::none() - : Limit::perHour(1)->by($job->user->id); - }); + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + RateLimiter::for('backups', function ($job) { + return $job->user->vipCustomer() + ? Limit::none() + : Limit::perHour(1)->by($job->user->id); + }); + } + +In the example above, we defined an hourly rate limit; however, you may easily define a rate limit based on minutes using the `perMinute` method. In addition, you may pass any value you wish to the `by` method of the rate limit; however, this value is most often used to segment rate limits by customer: + + return Limit::perMinute(50)->by($job->user->id); -You may then attach the rate limiter to your backup job using the `RateLimited` middleware. Each time the job exceeds the rate limit, this middleware will release the job back to the queue with an appropriate delay based on the rate limit duration. +Once you have defined your rate limit, you may attach the rate limiter to your backup job using the `Illuminate\Queue\Middleware\RateLimited` middleware. Each time the job exceeds the rate limit, this middleware will release the job back to the queue with an appropriate delay based on the rate limit duration. use Illuminate\Queue\Middleware\RateLimited; @@ -409,14 +423,14 @@ You may then attach the rate limiter to your backup job using the `RateLimited` Releasing a rate limited job back onto the queue will still increment the job's total number of `attempts`. You may wish to tune your `tries` and `maxExceptions` properties on your job class accordingly. Or, you may wish to use the [`retryUntil` method](#time-based-attempts) to define the amount of time until the job should no longer be attempted. -> {tip} If you are using Redis, you may use the `RateLimitedWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic rate limiting middleware. +> {tip} If you are using Redis, you may use the `Illuminate\Queue\Middleware\RateLimitedWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic rate limiting middleware. <a name="preventing-job-overlaps"></a> ### Preventing Job Overlaps -Laravel includes a `WithoutOverlapping` middleware that allows you to prevent job overlaps based on an arbitrary key. This can be helpful when a queued job is modifying a resource that should only be modified by one job at a time. +Laravel includes an `Illuminate\Queue\Middleware\WithoutOverlapping` middleware that allows you to prevent job overlaps based on an arbitrary key. This can be helpful when a queued job is modifying a resource that should only be modified by one job at a time. -For example, let's imagine you have a refund processing job and you want to prevent refund job overlaps for the same order ID. To accomplish this, you can return the `WithoutOverlapping` middleware from your refund processing job's `middleware` method: +For example, let's imagine you have a queued job that updates a user's credit score and you want to prevent credit score update job overlaps for the same user ID. To accomplish this, you can return the `WithoutOverlapping` middleware from your job's `middleware` method: use Illuminate\Queue\Middleware\WithoutOverlapping; @@ -427,10 +441,10 @@ For example, let's imagine you have a refund processing job and you want to prev */ public function middleware() { - return [new WithoutOverlapping($this->order->id)]; + return [new WithoutOverlapping($this->user->id)]; } -Any overlapping jobs will be released back to the queue. You may also specify the number of seconds that must elapse before the job will be attempted again: +Any overlapping jobs will be released back to the queue. You may also specify the number of seconds that must elapse before the released job will be attempted again: /** * Get the middleware the job should pass through. @@ -442,7 +456,7 @@ Any overlapping jobs will be released back to the queue. You may also specify th return [(new WithoutOverlapping($this->order->id))->releaseAfter(60)]; } -If you wish to immediately delete any overlapping jobs, you may use the `dontRelease` method: +If you wish to immediately delete any overlapping jobs so that they will not be retried, you may use the `dontRelease` method: /** * Get the middleware the job should pass through. @@ -454,7 +468,7 @@ If you wish to immediately delete any overlapping jobs, you may use the `dontRel return [(new WithoutOverlapping($this->order->id))->dontRelease()]; } -> {note} The `WithoutOverlapping` middleware requires a cache driver that supports [locks](/docs/{{version}}/cache#atomic-locks). +> {note} The `WithoutOverlapping` middleware requires a cache driver that supports [locks](/docs/{{version}}/cache#atomic-locks). Currently, the `memcached`, `redis`, `dynamodb`, `database`, `file`, and `array` cache drivers support atomic locks. <a name="dispatching-jobs"></a> ## Dispatching Jobs From 0aa9329dc476b57673715da430e1b5b4b9deb446 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 16:47:00 -0600 Subject: [PATCH 125/274] wip --- events.md | 39 ++++++++++++++++++++++++++ queues.md | 82 +++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 91 insertions(+), 30 deletions(-) diff --git a/events.md b/events.md index ec964f3b8f..010a2805c8 100644 --- a/events.md +++ b/events.md @@ -444,6 +444,45 @@ Sometimes your queued event listeners may fail. If queued listener exceeds the m } } +<a name="specifying-queued-listener-maximum-attempts"></a> +#### Specifying Queued Listener Maximum Attempts + +If one of your queued listener is encountering an error, you likely do not want it to keep retrying indefinitely. Therefore, Laravel provides various ways to specify how many times or for how long a listener may be attempted. + +You may define `$tries` property on your listener class to specify how many times the listener may be attempted before it is considered to have failed: + + <?php + + namespace App\Listeners; + + use App\Events\OrderShipped; + use Illuminate\Contracts\Queue\ShouldQueue; + use Illuminate\Queue\InteractsWithQueue; + + class SendShipmentNotification implements ShouldQueue + { + use InteractsWithQueue; + + /** + * The number of times the queued listener may be attempted. + * + * @var int + */ + public $tries = 5; + } + +As an alternative to defining how many times a listener may be attempted before it fails, you may define a time at which the listener should no longer be attempted. This allows a listener to be attempted any number of times within a given time frame. To define the time at which a listener should no longer be attempted, add a `retryUntil` method to your listener class. This method should return a `DateTime` instance: + + /** + * Determine the time at which the listener should timeout. + * + * @return \DateTime + */ + public function retryUntil() + { + return now()->addMinutes(5); + } + <a name="dispatching-events"></a> ## Dispatching Events diff --git a/queues.md b/queues.md index ef1b96ab8b..d1feccba88 100644 --- a/queues.md +++ b/queues.md @@ -481,6 +481,7 @@ Once you have written your job class, you may dispatch it using the `dispatch` m use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; + use App\Models\Podcast; use Illuminate\Http\Request; class PodcastController extends Controller @@ -488,12 +489,14 @@ Once you have written your job class, you may dispatch it using the `dispatch` m /** * Store a new podcast. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function store(Request $request) { - // Create podcast... + $podcast = Podcast::create(...); + + // ... ProcessPodcast::dispatch($podcast); } @@ -501,14 +504,14 @@ Once you have written your job class, you may dispatch it using the `dispatch` m If you would like to conditionally dispatch a job, you may use the `dispatchIf` and `dispatchUnless` methods: - ProcessPodcast::dispatchIf($accountActive === true, $podcast); + ProcessPodcast::dispatchIf($accountActive, $podcast); - ProcessPodcast::dispatchUnless($accountSuspended === false, $podcast); + ProcessPodcast::dispatchUnless($accountSuspended, $podcast); <a name="delayed-dispatching"></a> ### Delayed Dispatching -If you would like to delay the execution of a queued job, you may use the `delay` method when dispatching a job. For example, let's specify that a job should not be available for processing until 10 minutes after it has been dispatched: +If you would like to specify that a job should not be immediately available for processing by a queue worker, you may use the `delay` method when dispatching the job. For example, let's specify that a job should not be available for processing until 10 minutes after it has been dispatched: <?php @@ -516,6 +519,7 @@ If you would like to delay the execution of a queued job, you may use the `delay use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; + use App\Models\Podcast; use Illuminate\Http\Request; class PodcastController extends Controller @@ -523,15 +527,17 @@ If you would like to delay the execution of a queued job, you may use the `delay /** * Store a new podcast. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function store(Request $request) { - // Create podcast... + Podcast::create(...); + + // ... ProcessPodcast::dispatch($podcast) - ->delay(now()->addMinutes(10)); + ->delay(now()->addMinutes(10)); } } @@ -540,25 +546,25 @@ If you would like to delay the execution of a queued job, you may use the `delay <a name="dispatching-after-the-response-is-sent-to-browser"></a> #### Dispatching After The Response Is Sent To Browser -Alternatively, the `dispatchAfterResponse` method delays dispatching a job until after the response is sent to the user's browser. This will still allow the user to begin using the application even though a queued job is still executing. This should typically only be used for jobs that take about a second, such as sending an email: +Alternatively, the `dispatchAfterResponse` method delays dispatching a job until after the HTTP response is sent to the user's browser. This will still allow the user to begin using the application even though a queued job is still executing. This should typically only be used for jobs that take about a second, such as sending an email. Since they are processed within the current HTTP request, jobs dispatched in this fashion do not require a queue worker to be running in order for them to be processed: use App\Jobs\SendNotification; SendNotification::dispatchAfterResponse(); -You may `dispatch` a closure and chain the `afterResponse` method onto the helper to execute a closure after the response has been sent to the browser: +You may also `dispatch` a closure and chain the `afterResponse` method onto the `dispatch` helper to execute a closure after the HTTP response has been sent to the browser: use App\Mail\WelcomeMessage; use Illuminate\Support\Facades\Mail; dispatch(function () { - Mail::to('taylor@laravel.com')->send(new WelcomeMessage); + Mail::to('taylor@example.com')->send(new WelcomeMessage); })->afterResponse(); <a name="synchronous-dispatching"></a> ### Synchronous Dispatching -If you would like to dispatch a job immediately (synchronously), you may use the `dispatchSync` method. When using this method, the job will not be queued and will be run immediately within the current process: +If you would like to dispatch a job immediately (synchronously), you may use the `dispatchSync` method. When using this method, the job will not be queued and will be executed immediately within the current process: <?php @@ -566,6 +572,7 @@ If you would like to dispatch a job immediately (synchronously), you may use the use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; + use App\Models\Podcast; use Illuminate\Http\Request; class PodcastController extends Controller @@ -573,11 +580,13 @@ If you would like to dispatch a job immediately (synchronously), you may use the /** * Store a new podcast. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function store(Request $request) { + $podcast = Podcast::create(...); + // Create podcast... ProcessPodcast::dispatchSync($podcast); @@ -587,8 +596,11 @@ If you would like to dispatch a job immediately (synchronously), you may use the <a name="job-chaining"></a> ### Job Chaining -Job chaining allows you to specify a list of queued jobs that should be run in sequence after the primary job has executed successfully. If one job in the sequence fails, the rest of the jobs will not be run. To execute a queued job chain, you may use the `chain` method provided by the `Bus` facade: +Job chaining allows you to specify a list of queued jobs that should be run in sequence after the primary job has executed successfully. If one job in the sequence fails, the rest of the jobs will not be run. To execute a queued job chain, you may use the `chain` method provided by the `Bus` facade. Laravel's command bus is a lower level component that queued job dispatching is built on top of: + use App\Jobs\OptimizePodcast; + use App\Jobs\ProcessPodcast; + use App\Jobs\ReleasePodcast; use Illuminate\Support\Facades\Bus; Bus::chain([ @@ -607,7 +619,7 @@ In addition to chaining job class instances, you may also chain closures: }, ])->dispatch(); -> {note} Deleting jobs using the `$this->delete()` method will not prevent chained jobs from being processed. The chain will only stop executing if a job in the chain fails. +> {note} Deleting jobs using the `$this->delete()` method within the job will not prevent chained jobs from being processed. The chain will only stop executing if a job in the chain fails. <a name="chain-connection-queue"></a> #### Chain Connection & Queue @@ -623,7 +635,7 @@ If you would like to specify the connection and queue that should be used for th <a name="chain-failures"></a> #### Chain Failures -When chaining jobs, you may use the `catch` method to specify a closure that should be invoked if a job within the chain fails. The given callback will receive the exception instance that caused the job failure: +When chaining jobs, you may use the `catch` method to specify a closure that should be invoked if a job within the chain fails. The given callback will receive the `Throwable` instance that caused the job failure: use Illuminate\Support\Facades\Bus; use Throwable; @@ -650,6 +662,7 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; + use App\Models\Podcast; use Illuminate\Http\Request; class PodcastController extends Controller @@ -657,11 +670,13 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e /** * Store a new podcast. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function store(Request $request) { + $podcast = Podcast::create(...); + // Create podcast... ProcessPodcast::dispatch($podcast)->onQueue('processing'); @@ -671,7 +686,7 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e <a name="dispatching-to-a-particular-connection"></a> #### Dispatching To A Particular Connection -If you are working with multiple queue connections, you may specify which connection to push a job to. To specify the connection, use the `onConnection` method when dispatching the job: +If your application interacts with multiple queue connections, you may specify which connection to push a job to using the `onConnection` method: <?php @@ -679,6 +694,7 @@ If you are working with multiple queue connections, you may specify which connec use App\Http\Controllers\Controller; use App\Jobs\ProcessPodcast; + use App\Models\Podcast; use Illuminate\Http\Request; class PodcastController extends Controller @@ -686,18 +702,20 @@ If you are working with multiple queue connections, you may specify which connec /** * Store a new podcast. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function store(Request $request) { + $podcast = Podcast::create(...); + // Create podcast... ProcessPodcast::dispatch($podcast)->onConnection('sqs'); } } -You may chain the `onConnection` and `onQueue` methods to specify the connection and the queue for a job: +You may chain the `onConnection` and `onQueue` methods together to specify the connection and the queue for a job: ProcessPodcast::dispatch($podcast) ->onConnection('sqs') @@ -709,11 +727,15 @@ You may chain the `onConnection` and `onQueue` methods to specify the connection <a name="max-attempts"></a> #### 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: +If one of your queued jobs is encountering an error, you likely do not want it to keep retrying indefinitely. Therefore, Laravel provides various ways to specify how many times or for how long a job may be attempted. + +One approach to specifying the maximum number of times a job may be attempted is via the `--tries` switch on the Artisan command line. This will apply to all jobs processed by the worker unless the job being processed specifies a more specific number of times it may be attempted: 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: +If a job exceeds its maximum number of attempts, it will be considered a "failed" job. For more information on handling failed jobs, consult the [failed job documentation](#dealing-with-failed-jobs). + +You may take a more granular approach by defining the maximum number of times a job may be attempted on the job class itself. If the maximum number of attempts is specified on the job, it will take precedence over the `--tries` value provided on the command line: <?php @@ -732,7 +754,7 @@ However, you may take a more granular approach by defining the maximum number of <a name="time-based-attempts"></a> #### Time Based Attempts -As an alternative to defining how many times a job may be attempted before it fails, you may define a time at which the job should timeout. This allows a job to be attempted any number of times within a given time frame. To define the time at which a job should timeout, add a `retryUntil` method to your job class: +As an alternative to defining how many times a job may be attempted before it fails, you may define a time at which the job should no longer be attempted. This allows a job to be attempted any number of times within a given time frame. To define the time at which a job should no longer be attempted, add a `retryUntil` method to your job class. This method should return a `DateTime` instance: /** * Determine the time at which the job should timeout. @@ -741,10 +763,10 @@ As an alternative to defining how many times a job may be attempted before it fa */ public function retryUntil() { - return now()->addSeconds(5); + return now()->addMinutes(10); } -> {tip} You may also define a `retryUntil` method on your queued event listeners. +> {tip} You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners). <a name="max-exceptions"></a> #### Max Exceptions From 616cd131850a30d5e9bf36e4c7f09d5e6c99c120 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 16:56:33 -0600 Subject: [PATCH 126/274] wip --- queues.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/queues.md b/queues.md index d1feccba88..848787985c 100644 --- a/queues.md +++ b/queues.md @@ -771,12 +771,14 @@ As an alternative to defining how many times a job may be attempted before it fa <a name="max-exceptions"></a> #### Max Exceptions -Sometimes you may wish to specify that a job may be attempted many times, but should fail if the retries are triggered by a given number of exceptions. To accomplish this, you may define a `maxExceptions` property on your job class: +Sometimes you may wish to specify that a job may be attempted many times, but should fail if the retries are triggered by a given number of unhandled exceptions (as opposed to being released by the `release` method directly). To accomplish this, you may define a `maxExceptions` property on your job class: <?php namespace App\Jobs; + use Illuminate\Support\Facades\Redis; + class ProcessPodcast implements ShouldQueue { /** @@ -787,7 +789,7 @@ Sometimes you may wish to specify that a job may be attempted many times, but sh public $tries = 25; /** - * The maximum number of exceptions to allow before failing. + * The maximum number of unhandled exceptions to allow before failing. * * @var int */ @@ -816,11 +818,15 @@ In this example, the job is released for ten seconds if the application is unabl > {note} The `pcntl` PHP extension must be installed in order to specify job timeouts. -Likewise, the maximum number of seconds that jobs can run may be specified using the `--timeout` switch on the Artisan command line: +Often, you know roughly how long you expect your queued jobs to take. For this reason, Laravel allows you to specify a "timeout" value. If a job is processing for longer than the number of seconds specified by the timeout value, the worker processing the job will exit with an error. Typically, the worker will be restarted automatically by a [process manager configured on your server](#supervisor-configuration). + +The maximum number of seconds that jobs can run may be specified using the `--timeout` switch on the Artisan command line: php artisan queue:work --timeout=30 -However, you may also define the maximum number of seconds a job should be allowed to run on the job class itself. If the timeout is specified on the job, it will take precedence over any timeout specified on the command line: +If the job exceeds its maximum attempts by continually timing out, it will be marked as failed. + +You may also define the maximum number of seconds a job should be allowed to run on the job class itself. If the timeout is specified on the job, it will take precedence over any timeout specified on the command line: <?php From 57504f478c1c5bd4941ab9d8e787d0c2dabf6ee7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 17:15:57 -0600 Subject: [PATCH 127/274] wip --- queues.md | 78 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/queues.md b/queues.md index 848787985c..e5dfd41d53 100644 --- a/queues.md +++ b/queues.md @@ -852,7 +852,7 @@ If an exception is thrown while the job is being processed, the job will automat <a name="job-batching"></a> ## Job Batching -Laravel's job batching feature allows you to easily execute a batch of jobs and then perform some action when the batch of jobs has completed executing. Before getting started, you should create a database migration to build a table that will contain your job batch meta information. This migration may be generated using the `queue:batches-table` Artisan command: +Laravel's job batching feature allows you to easily execute a batch of jobs and then perform some action when the batch of jobs has completed executing. Before getting started, you should create a database migration to build a table to contain meta information about your job batches, such as their completion percentage. This migration may be generated using the `queue:batches-table` Artisan command: php artisan queue:batches-table @@ -861,14 +861,12 @@ Laravel's job batching feature allows you to easily execute a batch of jobs and <a name="defining-batchable-jobs"></a> ### Defining Batchable Jobs -To build a batchable job, you should [create a queueable job](#creating-jobs) as normal; however, you should add the `Illuminate\Bus\Batchable` trait to the job class. This trait provides access to a `batch` method which may be used to retrieve the current batch that the job is executing in: +To define a batchable job, you should [create a queueable job](#creating-jobs) as normal; however, you should add the `Illuminate\Bus\Batchable` trait to the job class. This trait provides access to a `batch` method which may be used to retrieve the current batch that the job is executing within: <?php namespace App\Jobs; - use App\Models\Podcast; - use App\Services\AudioProcessor; use Illuminate\Bus\Batchable; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; @@ -876,7 +874,7 @@ To build a batchable job, you should [create a queueable job](#creating-jobs) as use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; - class ProcessPodcast implements ShouldQueue + class ImportCsv implements ShouldQueue { use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -888,32 +886,31 @@ To build a batchable job, you should [create a queueable job](#creating-jobs) as public function handle() { if ($this->batch()->cancelled()) { - // Detected cancelled batch... + // Determine if the batch has been cancelled... return; } - // Batched job executing... + // Import a portion of the CSV file... } } <a name="dispatching-batches"></a> ### Dispatching Batches -To dispatch a batch of jobs, you should use the `batch` method of the `Bus` facade. Of course, batching is primarily useful when combined with completion callbacks. So, you may use the `then`, `catch`, and `finally` methods to define completion callbacks for the batch. Each of these callbacks will receive an `Illuminate\Bus\Batch` instance when they are invoked: +To dispatch a batch of jobs, you should use the `batch` method of the `Bus` facade. Of course, batching is primarily useful when combined with completion callbacks. So, you may use the `then`, `catch`, and `finally` methods to define completion callbacks for the batch. Each of these callbacks will receive an `Illuminate\Bus\Batch` instance when they are invoked. In this example, we will imagine we are queueing a batch of jobs that each process a given number of rows from a CSV file: - use App\Jobs\ProcessPodcast; - use App\Podcast; + use App\Jobs\ImportCsv; use Illuminate\Bus\Batch; use Illuminate\Support\Facades\Bus; use Throwable; $batch = Bus::batch([ - new ProcessPodcast(Podcast::find(1)), - new ProcessPodcast(Podcast::find(2)), - new ProcessPodcast(Podcast::find(3)), - new ProcessPodcast(Podcast::find(4)), - new ProcessPodcast(Podcast::find(5)), + new ImportCsv(1, 100), + new ImportCsv(101, 200), + new ImportCsv(201, 300), + new ImportCsv(301, 400), + new ImportCsv(401, 500), ])->then(function (Batch $batch) { // All jobs completed successfully... })->catch(function (Batch $batch, Throwable $e) { @@ -924,6 +921,8 @@ To dispatch a batch of jobs, you should use the `batch` method of the `Bus` faca return $batch->id; +The batch's ID, which may be accessed via the `$batch->id` property, may be used to [query the Laravel command bus](#inspecting-batches) for information about the batch after it has been dispatched. + <a name="naming-batches"></a> #### Naming Batches @@ -933,23 +932,28 @@ Some tools such as Laravel Horizon and Laravel Telescope may provide more user-f // ... ])->then(function (Batch $batch) { // All jobs completed successfully... - })->name('Process Podcasts')->dispatch(); + })->name('Import CSV')->dispatch(); <a name="batch-connection-queue"></a> #### Batch Connection & Queue -If you would like to specify the connection and queue that should be used for the batched jobs, you may use the `onConnection` and `onQueue` methods: +If you would like to specify the connection and queue that should be used for the batched jobs, you may use the `onConnection` and `onQueue` methods. All batched jobs must execute within the same connection and queue: $batch = Bus::batch([ // ... ])->then(function (Batch $batch) { // All jobs completed successfully... - })->onConnection('redis')->onQueue('podcasts')->dispatch(); + })->onConnection('redis')->onQueue('imports')->dispatch(); <a name="chains-within-batches"></a> #### Chains Within Batches -You may add a set of [chained jobs](#job-chaining) within a batch by placing the chained jobs within an array. For example, we may execute two job chains in parallel. Since the two chains are batched, we will be able to inspect the batch completion progress as a whole: +You may define a set of [chained jobs](#job-chaining) within a batch by placing the chained jobs within an array. For example, we may execute two job chains in parallel and execute a callback when both job chains have finished processing: + + use App\Jobs\ReleasePodcast; + use App\Jobs\SendPodcastReleaseNotification; + use Illuminate\Bus\Batch; + use Illuminate\Support\Facades\Bus; Bus::batch([ [ @@ -960,12 +964,14 @@ You may add a set of [chained jobs](#job-chaining) within a batch by placing the new ReleasePodcast(2), new SendPodcastReleaseNotification(2), ], - ])->dispatch(); + ])->then(function (Batch $batch) { + // ... + })->dispatch(); <a name="adding-jobs-to-batches"></a> ### Adding Jobs To Batches -Sometimes it may be useful to add additional jobs to a batch from within a batched job. This pattern can be useful when you need to batch thousands of jobs which may take too long to dispatch during a web request. So, instead, you may wish to dispatch an initial batch of "loader" jobs that hydrate the batch with more jobs: +Sometimes it may be useful to add additional jobs to a batch from within a batched job. This pattern can be useful when you need to batch thousands of jobs which may take too long to dispatch during a web request. So, instead, you may wish to dispatch an initial batch of "loader" jobs that hydrate the batch with even more jobs: $batch = Bus::batch([ new LoadImportBatch, @@ -975,7 +981,7 @@ Sometimes it may be useful to add additional jobs to a batch from within a batch // All jobs completed successfully... })->name('Import Contacts')->dispatch(); -In this example, we will use the `LoadImportBatch` job to hydrate the batch with additional jobs. To accomplish this, we may use the `add` method on the batch instance that can be accessed within the job: +In this example, we will use the `LoadImportBatch` job to hydrate the batch with additional jobs. To accomplish this, we may use the `add` method on the batch instance that may be accessed via the job's `batch` method: use App\Jobs\ImportContacts; use Illuminate\Support\Collection; @@ -1001,7 +1007,7 @@ In this example, we will use the `LoadImportBatch` job to hydrate the batch with <a name="inspecting-batches"></a> ### Inspecting Batches -The `Illuminate\Bus\Batch` method that is provided to batch completion callbacks has a variety of properties and methods to assist you in interacting with and inspecting a given batch of jobs. +The `Illuminate\Bus\Batch` instance that is provided to batch completion callbacks has a variety of properties and methods to assist you in interacting with and inspecting a given batch of jobs: // The UUID of the batch... $batch->id; @@ -1036,7 +1042,9 @@ The `Illuminate\Bus\Batch` method that is provided to batch completion callbacks <a name="returning-batches-from-routes"></a> #### Returning Batches From Routes -All `Illuminate\Bus\Batch` instances are JSON serializable, meaning you can return them directly from one of your application's routes to retrieve a JSON payload containing information about the batch, including its completion progress. To retrieve a batch by its ID, you may use the `Bus` facade's `findBatch` method: +All `Illuminate\Bus\Batch` instances are JSON serializable, meaning you can return them directly from one of your application's routes to retrieve a JSON payload containing information about the batch, including its completion progress. This makes it convenient to display information about the batch's completion progress in your application's UI. + +To retrieve a batch by its ID, you may use the `Bus` facade's `findBatch` method: use Illuminate\Support\Facades\Bus; use Illuminate\Support\Facades\Route; @@ -1066,10 +1074,26 @@ Sometimes you may need to cancel a given batch's execution. This can be accompli } } +As you may have noticed in previous examples, batched jobs should typically check to see if the batch has been cancelled at the beginning of their `handle` method: + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + if ($this->batch()->cancelled()) { + return; + } + + // Continue processing... + } + <a name="batch-failures"></a> ### Batch Failures -When a batch job fails, the `catch` callback (if assigned) will be invoked. This callback is only invoked for the job that fails within the batch. +When a batched job fails, the `catch` callback (if assigned) will be invoked. This callback is only invoked for the first job that fails within the batch. <a name="allowing-failures"></a> #### Allowing Failures @@ -1087,7 +1111,9 @@ When a job within a batch fails, Laravel will automatically mark the batch as "c For convenience, Laravel provides a `queue:retry-batch` Artisan command that allows you to easily retry all of the failed jobs for a given batch. The `queue:retry-batch` command accepts the UUID of the batch whose failed jobs should be retried: - php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5 +```bash +php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5 +``` <a name="queueing-closures"></a> ## Queueing Closures From 671a620c023bd0aac385505fc7bff6b3adabdcca Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 17:33:13 -0600 Subject: [PATCH 128/274] wip --- queues.md | 57 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/queues.md b/queues.md index e5dfd41d53..db12cdfb41 100644 --- a/queues.md +++ b/queues.md @@ -26,6 +26,7 @@ - [Batch Failures](#batch-failures) - [Queueing Closures](#queueing-closures) - [Running The Queue Worker](#running-the-queue-worker) + - [The `queue:work` Command](#the-queue-work-command) - [Queue Priorities](#queue-priorities) - [Queue Workers & Deployment](#queue-workers-and-deployment) - [Job Expirations & Timeouts](#job-expirations-and-timeouts) @@ -1118,7 +1119,7 @@ php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5 <a name="queueing-closures"></a> ## Queueing Closures -Instead of dispatching a job class to the queue, you may also dispatch a closure. This is great for quick, simple tasks that need to be executed outside of the current request cycle. When dispatching closures to the queue, the closure's code contents is cryptographically signed so it can not be modified in transit: +Instead of dispatching a job class to the queue, you may also dispatch a closure. This is great for quick, simple tasks that need to be executed outside of the current request cycle. When dispatching closures to the queue, the closure's code content is cryptographically signed so that it can not be modified in transit: $podcast = App\Podcast::find(1); @@ -1126,7 +1127,7 @@ Instead of dispatching a job class to the queue, you may also dispatch a closure $podcast->publish(); }); -Using the `catch` method, you may provide a closure that should be executed if the queued closure fails to complete successfully after exhausting all of your queue's configured retry attempts: +Using the `catch` method, you may provide a closure that should be executed if the queued closure fails to complete successfully after exhausting all of your queue's [configured retry attempts](#max-job-attempts-and-timeout): use Throwable; @@ -1139,7 +1140,10 @@ Using the `catch` method, you may provide a closure that should be executed if t <a name="running-the-queue-worker"></a> ## Running The Queue Worker -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: +<a name="the-queue-work-command"></a> +### The `queue:work` Command + +Laravel includes an Artisan command that will start a queue worker and 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 @@ -1147,10 +1151,15 @@ Laravel includes a queue worker that will process new jobs as they are pushed on 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). In addition, remember that any static state created or modified by your application will not be automatically reset between jobs. -Alternatively, you may run the `queue:listen` command. When using the `queue:listen` command, you don't have to manually restart the worker when you want to reload your updated code or reset the application state; however, this command is not as efficient as `queue:work`: +Alternatively, you may run the `queue:listen` command. When using the `queue:listen` command, you don't have to manually restart the worker when you want to reload your updated code or reset the application state; however, this command is significantly less efficient than the `queue:work` command: php artisan queue:listen +<a name="running-multiple-queue-workers"></a> +#### Running Multiple Queue Workers + +To assign multiple workers to a queue and process jobs concurrently, you should simply start multiple `queue:work` processes. This can either be done locally via multiple tabs in your terminal or in production using your process manager's configuration settings. [When using Supervisor](#supervisor-configuration), you may use the `numprocs` configuration value. + <a name="specifying-the-connection-queue"></a> #### Specifying The Connection & Queue @@ -1169,34 +1178,41 @@ The `--once` option may be used to instruct the worker to only process a single php artisan queue:work --once -The `--max-jobs` option may be used to instruct the worker to process the given number of jobs and then exit. This option may be useful when combined with [Supervisor](#supervisor-configuration) so that your workers are automatically restarted after processing a given number of jobs: +The `--max-jobs` option may be used to instruct the worker to process the given number of jobs and then exit. This option may be useful when combined with [Supervisor](#supervisor-configuration) so that your workers are automatically restarted after processing a given number of jobs, releasing any memory they may have accumulated: php artisan queue:work --max-jobs=1000 <a name="processing-all-queued-jobs-then-exiting"></a> #### Processing All Queued Jobs & Then Exiting -The `--stop-when-empty` option may be used to instruct the worker to process all jobs and then exit gracefully. This option can be useful when working Laravel queues within a Docker container if you wish to shutdown the container after the queue is empty: +The `--stop-when-empty` option may be used to instruct the worker to process all jobs and then exit gracefully. This option can be useful when processing Laravel queues within a Docker container if you wish to shutdown the container after the queue is empty: php artisan queue:work --stop-when-empty <a name="processing-jobs-for-a-given-number-of-seconds"></a> #### Processing Jobs For A Given Number Of Seconds -The `--max-time` option may be used to instruct the worker to process jobs for the given number of seconds and then exit. This option may be useful when combined with [Supervisor](#supervisor-configuration) so that your workers are automatically restarted after processing jobs for a given amount of time: +The `--max-time` option may be used to instruct the worker to process jobs for the given number of seconds and then exit. This option may be useful when combined with [Supervisor](#supervisor-configuration) so that your workers are automatically restarted after processing jobs for a given amount of time, releasing any memory they may have accumulated: // Process jobs for one hour and then exit... php artisan queue:work --max-time=3600 +<a name="worker-sleep-duration"></a> +#### 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 any seconds the worker will "sleep" if there are no new jobs available. While sleeping, the worker will not process any new jobs - the jobs will be processed after the worker wakes up again. + + php artisan queue:work --sleep=3 + <a name="resource-considerations"></a> #### Resource Considerations -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. +Daemon queue workers do not "reboot" the framework before processing each job. Therefore, you should release 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 processing the image. <a name="queue-priorities"></a> ### Queue Priorities -Sometimes you may wish to prioritize how your queues are processed. For example, in your `config/queue.php` you may set the default `queue` for your `redis` connection to `low`. However, occasionally you may wish to push a job to a `high` priority queue like so: +Sometimes you may wish to prioritize how your queues are processed. For example, in your `config/queue.php` configuration file you may set the default `queue` for your `redis` connection to `low`. However, occasionally you may wish to push a job to a `high` priority queue like so: dispatch((new Job)->onQueue('high')); @@ -1207,13 +1223,13 @@ To start a worker that verifies that all of the `high` queue jobs are processed <a name="queue-workers-and-deployment"></a> ### 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 notice 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 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. +This command will instruct all queue workers to gracefully exit after they finish processing their current job so that no existing jobs are lost. Since the queue workers will exit 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. -> {tip} The queue uses the [cache](/docs/{{version}}/cache) to store restart signals, so you should verify a cache driver is properly configured for your application before using this feature. +> {tip} The queue uses the [cache](/docs/{{version}}/cache) to store restart signals, so you should verify that a cache driver is properly configured for your application before using this feature. <a name="job-expirations-and-timeouts"></a> ### Job Expirations & Timeouts @@ -1221,27 +1237,22 @@ This command will instruct all queue workers to gracefully "die" after they fini <a name="job-expiration"></a> #### Job Expiration -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. +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 released or 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. 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. <a name="worker-timeouts"></a> #### 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. The `--timeout` option removes frozen processes that have exceeded that specified time limit: +The `queue:work` Artisan command exposes a `--timeout` option. If a job is processing for longer than the number of seconds specified by the timeout value, the worker processing the job will exit with an error. Typically, the worker will be restarted automatically by a [process manager configured on your server](#supervisor-configuration): - php artisan queue:work --timeout=60 +```bash +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. - -<a name="worker-sleep-duration"></a> -#### 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 (in seconds) the worker will "sleep" if there are no new jobs available. While sleeping, the worker will not process any new jobs - the jobs will be processed after the worker wakes up again. - - php artisan queue:work --sleep=3 +> {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 frozen job is always terminated before the job is retried. If your `--timeout` option is longer than your `retry_after` configuration value, your jobs may be processed twice. <a name="supervisor-configuration"></a> ## Supervisor Configuration From 0217b37e38e0d10316ad4c3c2ef110d8ad66af1d Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Thu, 19 Nov 2020 17:48:58 -0600 Subject: [PATCH 129/274] wip --- queues.md | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/queues.md b/queues.md index db12cdfb41..33497c1495 100644 --- a/queues.md +++ b/queues.md @@ -1257,32 +1257,40 @@ The `retry_after` configuration option and the `--timeout` CLI option are differ <a name="supervisor-configuration"></a> ## Supervisor Configuration +In production, you need a way to keep your `queue:work` processes running. A `queue:work` process may stop running for a variety of reasons, such as an exceeded worker timeout or the execution of the `queue:restart` command. + +For this reason, you need to configure a process monitor that can detect when your `queue:work` processes exit and automatically restart them. In addition, process monitors can allow you to specify how many `queue:work` processes you would like to run concurrently. Supervisor is a process monitor commonly used in Linux environments and we will discuss how to configure it in the following documentation. + <a name="installing-supervisor"></a> #### Installing Supervisor -Supervisor is a process monitor for the Linux operating system, and will automatically restart your `queue:work` process if it fails. To install Supervisor on Ubuntu, you may use the following command: +Supervisor is a process monitor for the Linux operating system, and will automatically restart your `queue:work` processes if they fail. To install Supervisor on Ubuntu, you may use the following command: sudo apt-get install supervisor -> {tip} If configuring Supervisor yourself sounds overwhelming, consider using [Laravel Forge](https://forge.laravel.com), which will automatically install and configure Supervisor for your Laravel projects. +> {tip} If configuring and managing Supervisor yourself sounds overwhelming, consider using [Laravel Forge](https://forge.laravel.com), which will automatically install and configure Supervisor for your production Laravel projects. <a name="configuring-supervisor"></a> #### Configuring Supervisor -Supervisor configuration files are typically stored in the `/etc/supervisor/conf.d` directory. Within this directory, you may create any number of configuration files that instruct supervisor how your processes should be monitored. For example, let's create a `laravel-worker.conf` file that starts and monitors a `queue:work` process: - - [program:laravel-worker] - process_name=%(program_name)s_%(process_num)02d - command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --max-time=3600 - autostart=true - autorestart=true - user=forge - numprocs=8 - redirect_stderr=true - stdout_logfile=/home/forge/app.com/worker.log - stopwaitsecs=3600 +Supervisor configuration files are typically stored in the `/etc/supervisor/conf.d` directory. Within this directory, you may create any number of configuration files that instruct supervisor how your processes should be monitored. For example, let's create a `laravel-worker.conf` file that starts and monitors `queue:work` processes: + +```ini +[program:laravel-worker] +process_name=%(program_name)s_%(process_num)02d +command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --max-time=3600 +autostart=true +autorestart=true +stopasgroup=true +killasgroup=true +user=forge +numprocs=8 +redirect_stderr=true +stdout_logfile=/home/forge/app.com/worker.log +stopwaitsecs=3600 +``` -In this example, the `numprocs` directive will instruct Supervisor to run 8 `queue:work` processes and monitor all of them, automatically restarting them if they fail. You should change the `queue:work sqs` portion of the `command` directive to reflect your desired queue connection. +In this example, the `numprocs` directive will instruct Supervisor to run eight `queue:work` processes and monitor all of them, automatically restarting them if they fail. You should change the `command` directive of the configuration to reflect your desired queue connection and worker options. > {note} You should ensure that the value of `stopwaitsecs` is greater than the number of seconds consumed by your longest running job. Otherwise, Supervisor may kill the job before it is finished processing. @@ -1291,11 +1299,13 @@ In this example, the `numprocs` directive will instruct Supervisor to run 8 `que Once the configuration file has been created, you may update the Supervisor configuration and start the processes using the following commands: - sudo supervisorctl reread +```bash +sudo supervisorctl reread - sudo supervisorctl update +sudo supervisorctl update - sudo supervisorctl start laravel-worker:* +sudo supervisorctl start laravel-worker:* +``` For more information on Supervisor, consult the [Supervisor documentation](http://supervisord.org/index.html). From 6870df58f684adc36645f140f2ec9043f5cabef2 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 09:00:33 -0600 Subject: [PATCH 130/274] wip --- queues.md | 114 +++++++++++++++++++++++++++++------------------------- 1 file changed, 61 insertions(+), 53 deletions(-) diff --git a/queues.md b/queues.md index 33497c1495..18f5debbeb 100644 --- a/queues.md +++ b/queues.md @@ -33,9 +33,9 @@ - [Supervisor Configuration](#supervisor-configuration) - [Dealing With Failed Jobs](#dealing-with-failed-jobs) - [Cleaning Up After Failed Jobs](#cleaning-up-after-failed-jobs) - - [Failed Job Events](#failed-job-events) - [Retrying Failed Jobs](#retrying-failed-jobs) - [Ignoring Missing Models](#ignoring-missing-models) + - [Failed Job Events](#failed-job-events) - [Clearing Jobs From Queues](#clearing-jobs-from-queues) - [Job Events](#job-events) @@ -1312,21 +1312,21 @@ For more information on Supervisor, consult the [Supervisor documentation](http: <a name="dealing-with-failed-jobs"></a> ## Dealing With Failed Jobs -Sometimes your queued jobs will fail. Don't worry, things don't always go as planned! Laravel includes a convenient way to specify the maximum number of times a job should be attempted. After a job has exceeded this amount of attempts, it will be inserted into the `failed_jobs` database table. To create a migration for the `failed_jobs` table, you may use the `queue:failed-table` command: +Sometimes your queued jobs will fail. Don't worry, things don't always go as planned! Laravel includes a convenient way to [specify the maximum number of times a job should be attempted](#max-job-attempts-and-timeout). After a job has exceeded this number of attempts, it will be inserted into the `failed_jobs` database table. Of course, we will need to create that table if it does not already exist. To create a migration for the `failed_jobs` table, you may use the `queue:failed-table` command: php artisan queue:failed-table php artisan migrate -Then, when running your [queue worker](#running-the-queue-worker), you can specify the maximum number of times a job should be attempted using the `--tries` switch on the `queue:work` command. If you do not specify a value for the `--tries` option, jobs will only be attempted once: +When running a [queue worker](#running-the-queue-worker) process, you may specify the maximum number of times a job should be attempted using the `--tries` switch on the `queue:work` command. If you do not specify a value for the `--tries` option, jobs will only be attempted once or as many times as specified by the job class' `$tries` property: php artisan queue:work redis --tries=3 -In addition, you may specify how many seconds Laravel should wait before retrying a job that has failed using the `--backoff` option. By default, a job is retried immediately: +Using the `--backoff` option, you may specify how many seconds Laravel should wait before retrying a job that has encountered an exception. By default, a job is immediately released back onto the queue so that it may be attempted again: php artisan queue:work redis --tries=3 --backoff=3 -If you would like to configure the failed job retry delay on a per-job basis, you may do so by defining a `backoff` property on your queued job class: +If you would like to configure how many seconds Laravel should wait before retrying a job that has encountered an exception on a per-job basis, you may do so by defining a `backoff` property on your job class: /** * The number of seconds to wait before retrying the job. @@ -1335,7 +1335,7 @@ If you would like to configure the failed job retry delay on a per-job basis, yo */ public $backoff = 3; -If you require more complex logic for determining the retry delay, you may define a `backoff` method on your queued job class: +If you require more complex logic for determining the job's backoff time, you may define a `backoff` method on your job class: /** * Calculate the number of seconds to wait before retrying the job. @@ -1347,7 +1347,7 @@ If you require more complex logic for determining the retry delay, you may defin return 3; } -You may easily configure "exponential" backoffs by returning an array of backoff values from the `backoff` method. In this example, the retry delay will be 1 seconds for the first retry, 5 seconds for the second retry, and 10 seconds for the third retry: +You may easily configure "exponential" backoffs by returning an array of backoff values from the `backoff` method. In this example, the retry delay will be 1 second for the first retry, 5 seconds for the second retry, and 10 seconds for the third retry: /** * Calculate the number of seconds to wait before retrying the job. @@ -1362,7 +1362,7 @@ You may easily configure "exponential" backoffs by returning an array of backoff <a name="cleaning-up-after-failed-jobs"></a> ### Cleaning Up After Failed Jobs -You may define a `failed` method directly on your job class, allowing you to perform job specific clean-up when a failure occurs. This is the perfect location to send an alert to your users or revert any actions performed by the job. The `Throwable` exception that caused the job to fail will be passed to the `failed` method: +When a particular job fails, you may want to send an alert to your users or revert any actions that were partially completed by the job. To accomplish this, you may define a `failed` method on your job class. The `Throwable` instance that caused the job to fail will be passed to the `failed` method: <?php @@ -1380,6 +1380,11 @@ You may define a `failed` method directly on your job class, allowing you to per { use InteractsWithQueue, Queueable, SerializesModels; + /** + * The podcast instance. + * + * @var \App\Podcast + */ protected $podcast; /** @@ -1416,50 +1421,10 @@ You may define a `failed` method directly on your job class, allowing you to per } } -<a name="failed-job-events"></a> -### Failed Job Events - -If you would like to register an event that will be called when a job fails, you may use the `Queue::failing` method. This event is a great opportunity to notify your team via email or [Slack](https://www.slack.com). For example, we may attach a callback to this event from the `AppServiceProvider` that is included with Laravel: - - <?php - - namespace App\Providers; - - use Illuminate\Support\Facades\Queue; - use Illuminate\Support\ServiceProvider; - use Illuminate\Queue\Events\JobFailed; - - class AppServiceProvider extends ServiceProvider - { - /** - * Register any application services. - * - * @return void - */ - public function register() - { - // - } - - /** - * Bootstrap any application services. - * - * @return void - */ - public function boot() - { - Queue::failing(function (JobFailed $event) { - // $event->connectionName - // $event->job - // $event->exception - }); - } - } - <a name="retrying-failed-jobs"></a> ### Retrying Failed Jobs -To view all of your failed jobs that have been inserted into your `failed_jobs` database table, you may use the `queue:failed` Artisan command: +To view all of the failed jobs that have been inserted into your `failed_jobs` database table, you may use the `queue:failed` Artisan command: php artisan queue:failed @@ -1483,16 +1448,16 @@ If you would like to delete a failed job, you may use the `queue:forget` command > {tip} When using [Horizon](/docs/{{version}}/horizon), you should use the `horizon:forget` command to delete a failed job instead of the `queue:forget` command. -To delete all of your failed jobs, you may use the `queue:flush` command: +To delete all of your failed jobs from the `failed_jobs` table, you may use the `queue:flush` command: php artisan queue:flush <a name="ignoring-missing-models"></a> ### Ignoring Missing Models -When injecting an Eloquent model into a job, it is automatically serialized before being placed on the queue and restored when the job is processed. However, if the model has been deleted while the job was waiting to be processed by a worker, your job may fail with a `ModelNotFoundException`. +When injecting an Eloquent model into a job, the model is automatically serialized before being placed on the queue and re-retrieved from the database when the job is processed. However, if the model has been deleted while the job was waiting to be processed by a worker, your job may fail with a `ModelNotFoundException`. -For convenience, you may choose to automatically delete jobs with missing models by setting your job's `deleteWhenMissingModels` property to `true`: +For convenience, you may choose to automatically delete jobs with missing models by setting your job's `deleteWhenMissingModels` property to `true`. When this model is set to `true`, Laravel will quietly discard the job without raising an exception: /** * Delete the job if its models no longer exist. @@ -1501,6 +1466,46 @@ For convenience, you may choose to automatically delete jobs with missing models */ public $deleteWhenMissingModels = true; +<a name="failed-job-events"></a> +### Failed Job Events + +If you would like to register an event listener that will be invoked when a job fails, you may use the `Queue` facade's `failing` method. For example, we may attach a closure to this event from the `boot` method of the `AppServiceProvider` that is included with Laravel: + + <?php + + namespace App\Providers; + + use Illuminate\Support\Facades\Queue; + use Illuminate\Support\ServiceProvider; + use Illuminate\Queue\Events\JobFailed; + + class AppServiceProvider extends ServiceProvider + { + /** + * Register any application services. + * + * @return void + */ + public function register() + { + // + } + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + Queue::failing(function (JobFailed $event) { + // $event->connectionName + // $event->job + // $event->exception + }); + } + } + <a name="clearing-jobs-from-queues"></a> ## Clearing Jobs From Queues @@ -1519,7 +1524,7 @@ You may also provide the `connection` argument and `queue` option to delete jobs <a name="job-events"></a> ## Job Events -Using the `before` and `after` methods on the `Queue` [facade](/docs/{{version}}/facades), you may specify callbacks to be executed before or after a queued job is processed. These callbacks are a great opportunity to perform additional logging or increment statistics for a dashboard. Typically, you should call these methods from a [service provider](/docs/{{version}}/providers). For example, we may use the `AppServiceProvider` that is included with Laravel: +Using the `before` and `after` methods on the `Queue` [facade](/docs/{{version}}/facades), you may specify callbacks to be executed before or after a queued job is processed. These callbacks are a great opportunity to perform additional logging or increment statistics for a dashboard. Typically, you should call these methods from the `boot` method of a [service provider](/docs/{{version}}/providers). For example, we may use the `AppServiceProvider` that is included with Laravel: <?php @@ -1565,6 +1570,9 @@ 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: + use Illuminate\Support\Facades\DB; + use Illuminate\Support\Facades\Queue; + Queue::looping(function () { while (DB::transactionLevel() > 0) { DB::rollBack(); From 193c551b7b44033195671179692b3e479df17880 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 10:00:25 -0600 Subject: [PATCH 131/274] wip --- scheduling.md | 128 +++++++++++++++++++++++++++++--------------------- 1 file changed, 74 insertions(+), 54 deletions(-) diff --git a/scheduling.md b/scheduling.md index 8ab45f862f..3a0a7ebb11 100644 --- a/scheduling.md +++ b/scheduling.md @@ -11,36 +11,22 @@ - [Running Tasks On One Server](#running-tasks-on-one-server) - [Background Tasks](#background-tasks) - [Maintenance Mode](#maintenance-mode) +- [Running The Scheduler](#running-the-scheduler) + - [Running The Scheduler Locally](#running-the-scheduler-locally) - [Task Output](#task-output) - [Task Hooks](#task-hooks) <a name="introduction"></a> ## Introduction -In the past, you may have generated a Cron entry for each task you needed to schedule on your server. However, this can quickly become a pain, because your task schedule is no longer in source control and you must SSH into your server to add additional Cron entries. +In the past, you may have written a cron configuration entry for each task you needed to schedule on your server. However, this can quickly become a pain because your task schedule is no longer in source control and you must SSH into your server to view your existing cron entries or add additional entries. -Laravel's command scheduler allows you to fluently and expressively define your command schedule within Laravel itself. When using the scheduler, only a single Cron entry is needed on your server. Your task schedule is defined in the `app/Console/Kernel.php` file's `schedule` method. To help you get started, a simple example is defined within the method. - -<a name="starting-the-scheduler"></a> -### Starting The Scheduler - -When using the scheduler, you only need to add the following Cron entry to your server. If you do not know how to add Cron entries to your server, consider using a service such as [Laravel Forge](https://forge.laravel.com) which can manage the Cron entries for you: - - * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 - -This Cron will call the Laravel command scheduler every minute. When the `schedule:run` command is executed, Laravel will evaluate your scheduled tasks and run the tasks that are due. - -<a name="starting-the-scheduler-locally"></a> -### Starting The Scheduler Locally - -Typically, you would not add a scheduler Cron entry to your local development machine. Instead you may use the `schedule:work` Artisan command. This command will run in the foreground and invoke the scheduler every minute until you exit the command: - - php artisan schedule:work +Laravel's command scheduler offers a fresh approach to managing scheduled tasks on your server. The scheduler allows you to fluently and expressively define your command schedule within your Laravel application itself. When using the scheduler, only a single cron entry is needed on your server. Your task schedule is defined in the `app/Console/Kernel.php` file's `schedule` method. To help you get started, a simple example is defined within the method. <a name="defining-schedules"></a> ## Defining Schedules -You may define all of your scheduled tasks in the `schedule` method of the `App\Console\Kernel` class. To get started, let's look at an example of scheduling a task. In this example, we will schedule a closure to be called every day at midnight. Within the closure we will execute a database query to clear a table: +You may define all of your scheduled tasks in the `schedule` method of your application's `App\Console\Kernel` class. To get started, let's take a look at an example. In this example, we will schedule a closure to be called every day at midnight. Within the closure we will execute a database query to clear a table: <?php @@ -75,14 +61,18 @@ You may define all of your scheduled tasks in the `schedule` method of the `App\ } } -In addition to scheduling using closures, you may also use [invokable objects](https://secure.php.net/manual/en/language.oop5.magic.php#object.invoke). Invokable objects are simple PHP classes that contain an `__invoke` method: +In addition to scheduling using closures, you may also schedule [invokable objects](https://secure.php.net/manual/en/language.oop5.magic.php#object.invoke). Invokable objects are simple PHP classes that contain an `__invoke` method: $schedule->call(new DeleteRecentUsers)->daily(); <a name="scheduling-artisan-commands"></a> ### Scheduling Artisan Commands -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: +In addition to scheduling closures, you may also schedule [Artisan commands](/docs/{{version}}/artisan) and system commands. For example, you may use the `command` method to schedule an Artisan command using either the command's name or class. + +When scheduling Artisan commands using the command's class name, you may pass an array of additional command-line arguments that should be provided to the command when it is invoked: + + use App\Console\Commands\SendEmailCommand; $schedule->command('emails:send Taylor --force')->daily(); @@ -91,12 +81,18 @@ In addition to scheduling closure calls, you may also schedule [Artisan commands <a name="scheduling-queued-jobs"></a> ### Scheduling Queued Jobs -The `job` method may be used to schedule a [queued job](/docs/{{version}}/queues). This method provides a convenient way to schedule jobs without using the `call` method to manually create closures to queue the job: +The `job` method may be used to schedule a [queued job](/docs/{{version}}/queues). This method provides a convenient way to schedule queued jobs without using the `call` method to define closures to queue the job: + + use App\Jobs\Heartbeat; $schedule->job(new Heartbeat)->everyFiveMinutes(); - // Dispatch the job to the "heartbeats" queue... - $schedule->job(new Heartbeat, 'heartbeats')->everyFiveMinutes(); +Optional second and third arguments may be provided to the `job` method which specify the queue name and queue connection that should be used to queue the job: + + use App\Jobs\Heartbeat; + + // Dispatch the job to the "heartbeats" queue on the "sqs" connection... + $schedule->job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes(); <a name="scheduling-shell-commands"></a> ### Scheduling Shell Commands @@ -108,11 +104,11 @@ The `exec` method may be used to issue a command to the operating system: <a name="schedule-frequency-options"></a> ### Schedule Frequency Options -There are a variety of schedules you may assign to your task: +We've already seen a few examples of how you may configure a task to run at specified intervals. However, there are many more task schedule frequencies that you may assign to a task: Method | Description ------------- | ------------- -`->cron('* * * * *');` | Run the task on a custom Cron schedule +`->cron('* * * * *');` | Run the task on a custom cron schedule `->everyMinute();` | Run the task every minute `->everyTwoMinutes();` | Run the task every two minutes `->everyThreeMinutes();` | Run the task every three minutes @@ -139,9 +135,9 @@ Method | Description `->quarterly();` | Run the task on the first day of every quarter at 00:00 `->yearly();` | Run the task on the first day of every year at 00:00 `->yearlyOn(6, 1, '17:00');` | Run the task every year on June 1st at 17:00 -`->timezone('America/New_York');` | Set the timezone +`->timezone('America/New_York');` | Set the timezone for the task -These methods may be combined with additional constraints to create even more finely tuned schedules that only run on certain days of the week. For example, to schedule a command to run weekly on Monday: +These methods may be combined with additional constraints to create even more finely tuned schedules that only run on certain days of the week. For example, you may schedule a command to run weekly on Monday: // Run once per week on Monday at 1 PM... $schedule->call(function () { @@ -155,7 +151,7 @@ These methods may be combined with additional constraints to create even more fi ->timezone('America/Chicago') ->between('8:00', '17:00'); -Below is a list of the additional schedule constraints: +A list of additional schedule constraints may be found below: Method | Description ------------- | ------------- @@ -179,22 +175,30 @@ Method | Description The `days` method may be used to limit the execution of a task to specific days of the week. For example, you may schedule a command to run hourly on Sundays and Wednesdays: - $schedule->command('reminders:send') + $schedule->command('emails:send') ->hourly() ->days([0, 3]); +Alternatively, you may use the constants available on the `Illuminate\Console\Scheduling\Schedule` class when defining the days on which a task should run: + + use Illuminate\Console\Scheduling\Schedule; + + $schedule->command('emails:send') + ->hourly() + ->days([Schedule::SUNDAY, Schedule::WEDNESDAY]); + <a name="between-time-constraints"></a> #### 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') + $schedule->command('emails: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') + $schedule->command('emails:send') ->hourly() ->unlessBetween('23:00', '4:00'); @@ -218,7 +222,7 @@ When using chained `when` methods, the scheduled command will only execute if al <a name="environment-constraints"></a> #### Environment Constraints -The `environments` method may be used to execute tasks only on the given environments: +The `environments` method may be used to execute tasks only on the given environments (as defined by the `APP_ENV` [environment variable](/docs/{{version}}/configuration#environment-configuration)): $schedule->command('emails:send') ->daily() @@ -231,9 +235,9 @@ Using the `timezone` method, you may specify that a scheduled task's time should $schedule->command('report:generate') ->timezone('America/New_York') - ->at('02:00') + ->at('2:00') -If you are assigning the same timezone to all of your scheduled tasks, you may wish to define a `scheduleTimezone` method in your `app/Console/Kernel.php` file. This method should return the default timezone that should be assigned to all scheduled tasks: +If you are repeatedly assigning the same timezone to all of your scheduled tasks, you may wish to define a `scheduleTimezone` method in your `App\Console\Kernel` class. This method should return the default timezone that should be assigned to all scheduled tasks: /** * Get the timezone that should be used by default for scheduled events. @@ -263,9 +267,9 @@ If needed, you may specify how many minutes must pass before the "without overla <a name="running-tasks-on-one-server"></a> ### Running Tasks On One Server -> {note} To utilize this feature, your application must be using the `database`, `memcached`, or `redis` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. +> {note} To utilize this feature, your application must be using the `database`, `memcached`, `dynamodb`, or `redis` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. -If your application is running on multiple servers, you may limit a scheduled job to only execute on a single server. For instance, assume you have a scheduled task that generates a new report every Friday night. If the task scheduler is running on three worker servers, the scheduled task will run on all three servers and generate the report three times. Not good! +If your application's scheduler is running on multiple servers, you may limit a scheduled job to only execute on a single server. For instance, assume you have a scheduled task that generates a new report every Friday night. If the task scheduler is running on three worker servers, the scheduled task will run on all three servers and generate the report three times. Not good! To indicate that the task should run on only one server, use the `onOneServer` method when defining the scheduled task. The first server to obtain the task will secure an atomic lock on the job to prevent other servers from running the same task at the same time: @@ -277,7 +281,7 @@ To indicate that the task should run on only one server, use the `onOneServer` m <a name="background-tasks"></a> ### Background Tasks -By default, multiple commands scheduled at the same time will execute sequentially. If you have long-running commands, this may cause subsequent commands to start much later than anticipated. If you would like to run commands in the background so that they may all run simultaneously, you may use the `runInBackground` method: +By default, multiple tasks scheduled at the same time will execute sequentially based on the order they are defined in your `schedule` method. If you have long-running tasks, this may cause subsequent tasks to start much later than anticipated. If you would like to run tasks in the background so that they may all run simultaneously, you may use the `runInBackground` method: $schedule->command('analytics:report') ->daily() @@ -288,10 +292,26 @@ By default, multiple commands scheduled at the same time will execute sequential <a name="maintenance-mode"></a> ### 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: +Your application's scheduled tasks will not run when the application 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 call the `evenInMaintenanceMode` method when defining the task: $schedule->command('emails:send')->evenInMaintenanceMode(); +<a name="running-the-scheduler"></a> +## Running The Scheduler + +Now that we have learned how to define scheduled tasks, let's discuss how to actually run them on our server. The `schedule:run` Artisan command will evaluate all of your scheduled tasks and determine if they need to run based on the current time. + +So, when using Laravel's scheduler, we only need to add a single cron configuration entry to our server that runs the `schedule:run` command every minute. If you do not know how to add cron entries to your server, consider using a service such as [Laravel Forge](https://forge.laravel.com) which can manage the cron entries for you: + + * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1 + +<a name="running-the-scheduler-locally"></a> +## Running The Scheduler Locally + +Typically, you would not add a scheduler cron entry to your local development machine. Instead you may use the `schedule:work` Artisan command. This command will run in the foreground and invoke the scheduler every minute until you terminate the command: + + php artisan schedule:work + <a name="task-output"></a> ## Task Output @@ -307,36 +327,36 @@ 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. 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 email the output to an email address of your choice. Before emailing the output of a task, you should configure Laravel's [email services](/docs/{{version}}/mail): - $schedule->command('foo') + $schedule->command('report:generate') ->daily() ->sendOutputTo($filePath) - ->emailOutputTo('foo@example.com'); + ->emailOutputTo('taylor@example.com'); -If you only want to e-mail the output if the command fails, use the `emailOutputOnFailure` method: +If you only want to email the output if the scheduled Artisan or system command terminates with a non-zero exit code, use the `emailOutputOnFailure` method: - $schedule->command('foo') + $schedule->command('report:generate') ->daily() - ->emailOutputOnFailure('foo@example.com'); + ->emailOutputOnFailure('taylor@example.com'); > {note} The `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo`, and `appendOutputTo` methods are exclusive to the `command` and `exec` methods. <a name="task-hooks"></a> ## Task Hooks -Using the `before` and `after` methods, you may specify code to be executed before and after the scheduled task is complete: +Using the `before` and `after` methods, you may specify code to be executed before and after the scheduled task is executed: $schedule->command('emails:send') ->daily() ->before(function () { - // Task is about to start... + // The task is about to execute... }) ->after(function () { - // Task is complete... + // The task has executed... }); -The `onSuccess` and `onFailure` methods allow you to specify code to be executed if the scheduled task succeeds or fails: +The `onSuccess` and `onFailure` methods allow you to specify code to be executed if the scheduled task succeeds or fails. A failure indicates that the scheduled Artisan or system command terminated with a non-zero exit code: $schedule->command('emails:send') ->daily() @@ -347,7 +367,7 @@ The `onSuccess` and `onFailure` methods allow you to specify code to be executed // The task failed... }); -If output is available from your command, you may access it in your `after`, `onSuccess` or `onFailure` hooks by type-hinting an `Illuminate\Support\Stringable` instance as `$output` on your hook's closure definition: +If output is available from your command, you may access it in your `after`, `onSuccess` or `onFailure` hooks by type-hinting an `Illuminate\Support\Stringable` instance as the `$output` argument of your hook's closure definition: use Illuminate\Support\Stringable; @@ -363,27 +383,27 @@ If output is available from your command, you may access it in your `after`, `on <a name="pinging-urls"></a> #### Pinging URLs -Using the `pingBefore` and `thenPing` methods, the scheduler can automatically ping a given URL before or after a task is complete. This method is useful for notifying an external service, such as [Laravel Envoyer](https://envoyer.io), that your scheduled task is commencing or has finished execution: +Using the `pingBefore` and `thenPing` methods, the scheduler can automatically ping a given URL before or after a task is executed. This method is useful for notifying an external service, such as [Envoyer](https://envoyer.io), that your scheduled task is beginning or has finished execution: $schedule->command('emails:send') ->daily() ->pingBefore($url) ->thenPing($url); -The `pingBeforeIf` and `thenPingIf` methods may be used to ping a given URL only if the given condition is `true`: +The `pingBeforeIf` and `thenPingIf` methods may be used to ping a given URL only if a given condition is `true`: $schedule->command('emails:send') ->daily() ->pingBeforeIf($condition, $url) ->thenPingIf($condition, $url); -The `pingOnSuccess` and `pingOnFailure` methods may be used to ping a given URL only if the task succeeds or fails: +The `pingOnSuccess` and `pingOnFailure` methods may be used to ping a given URL only if the task succeeds or fails. A failure indicates that the scheduled Artisan or system command terminated with a non-zero exit code: $schedule->command('emails:send') ->daily() ->pingOnSuccess($successUrl) ->pingOnFailure($failureUrl); -All of the ping methods require the Guzzle HTTP library. You can add Guzzle to your project using the Composer package manager: +All of the ping methods require the Guzzle HTTP library. Guzzle is typically installed in all new Laravel projects by default, but, you may manually install Guzzle into your project using the Composer package manager if it has been accidentally removed: composer require guzzlehttp/guzzle From 78d132468375f0ba9678e0bbea31ae7826f928e0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 10:05:06 -0600 Subject: [PATCH 132/274] wip --- scheduling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduling.md b/scheduling.md index 3a0a7ebb11..e0f0b54090 100644 --- a/scheduling.md +++ b/scheduling.md @@ -299,7 +299,7 @@ Your application's scheduled tasks will not run when the application is in [main <a name="running-the-scheduler"></a> ## Running The Scheduler -Now that we have learned how to define scheduled tasks, let's discuss how to actually run them on our server. The `schedule:run` Artisan command will evaluate all of your scheduled tasks and determine if they need to run based on the current time. +Now that we have learned how to define scheduled tasks, let's discuss how to actually run them on our server. The `schedule:run` Artisan command will evaluate all of your scheduled tasks and determine if they need to run based on the server's current time. So, when using Laravel's scheduler, we only need to add a single cron configuration entry to our server that runs the `schedule:run` command every minute. If you do not know how to add cron entries to your server, consider using a service such as [Laravel Forge](https://forge.laravel.com) which can manage the cron entries for you: From f80d984efc16bc4501c0a0282f00212410057687 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 10:06:48 -0600 Subject: [PATCH 133/274] change link order --- authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication.md b/authentication.md index ceaca11f63..d321e95046 100644 --- a/authentication.md +++ b/authentication.md @@ -19,12 +19,12 @@ - [Configuration](#password-confirmation-configuration) - [Routing](#password-confirmation-routing) - [Protecting Routes](#password-confirmation-protecting-routes) -- [Social Authentication](/docs/{{version}}/socialite) - [Adding Custom Guards](#adding-custom-guards) - [Closure Request Guards](#closure-request-guards) - [Adding Custom User Providers](#adding-custom-user-providers) - [The User Provider Contract](#the-user-provider-contract) - [The Authenticatable Contract](#the-authenticatable-contract) +- [Social Authentication](/docs/{{version}}/socialite) - [Events](#events) <a name="introduction"></a> From c412934f9c1aafa535e3481936c3d09969070b58 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 11:33:30 -0600 Subject: [PATCH 134/274] wip --- authentication.md | 144 +++++++++++++++++++++++++++------------------- sanctum.md | 2 +- 2 files changed, 85 insertions(+), 61 deletions(-) diff --git a/authentication.md b/authentication.md index d321e95046..a35467edb7 100644 --- a/authentication.md +++ b/authentication.md @@ -1,6 +1,7 @@ # Authentication - [Introduction](#introduction) + - [Starter Kits](#starter-kits) - [Database Considerations](#introduction-database-considerations) - [Ecosystem Overview](#ecosystem-overview) - [Authentication Quickstart](#authentication-quickstart) @@ -30,27 +31,29 @@ <a name="introduction"></a> ## Introduction -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. +Many web applications provide a way for their users to authenticate with the application and "login". Implementing this feature in web applications can be a complex and potentially risky endeavor. For this reason, Laravel strives to give you the tools you need to implement authentication quickly, securely, and easily. At its core, Laravel's authentication facilities are made up of "guards" and "providers". Guards define how users are authenticated for each request. For example, Laravel ships with a `session` guard which maintains state using session storage and cookies. -Providers define how users are retrieved from your persistent storage. Laravel ships with support for retrieving users using Eloquent and the database query builder. However, you are free to define additional providers as needed for your application. +Providers define how users are retrieved from your persistent storage. Laravel ships with support for retrieving users using [Eloquent](/docs/{{version}}/eloquent) and the database query builder. However, you are free to define additional providers as needed for your application. -Don't worry if this all sounds confusing now! Many applications will never need to modify the default authentication configuration. +Your application's authentication configuration file is located at `config/auth.php`. This file contains several well documented options for tweaking the behavior of Laravel's authentication services. -<a name="getting-started-fast"></a> -#### Getting Started Fast +<a name="starter-kits"></a> +### Starter Kits Want to get started fast? Install a [Laravel application starter kit](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system! +**Even if you choose to not use a starter kit in your final Laravel application, installing the [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze) starter kit can be a wonderful opportunity to learn how to implement all of Laravel's authentication functionality in an actual Laravel project.** Since Laravel Breeze creates authentication controllers, routes, and views for you, you can examine the code within these files to learn how Laravel's authentication features may be implemented. + <a name="introduction-database-considerations"></a> ### Database Considerations -By default, Laravel includes an `App\Models\User` [Eloquent model](/docs/{{version}}/eloquent) in your `app/Models` directory. This model may be used with the default Eloquent authentication driver. If your application is not using Eloquent, you may use the `database` authentication driver which uses the Laravel query builder. +By default, Laravel includes an `App\Models\User` [Eloquent model](/docs/{{version}}/eloquent) in your `app/Models` directory. This model may be used with the default Eloquent authentication driver. If your application is not using Eloquent, you may use the `database` authentication provider which uses the Laravel query builder. -When building the database schema for the `App\Models\User` model, make sure the password column is at least 60 characters in length. Maintaining the default string column length of 255 characters would be a good choice. +When building the database schema for the `App\Models\User` model, make sure the password column is at least 60 characters in length. Of course, the `users` table migration that is included in new Laravel applications already creates a column that exceeds this length. -Also, you should verify that your `users` (or equivalent) table contains a nullable, string `remember_token` column of 100 characters. This column will be used to store a token for users that select the "remember me" option when logging into your application. +Also, you should verify that your `users` (or equivalent) table contains a nullable, string `remember_token` column of 100 characters. This column will be used to store a token for users that select the "remember me" option when logging into your application. Again, the default `users` table migration that is included in new Laravel applications already contains this column. <a name="ecosystem-overview"></a> ### Ecosystem Overview @@ -59,22 +62,22 @@ Laravel offers several packages related to authentication. Before continuing, we First, consider how authentication works. When using a web browser, a user will provide their username and password via a login form. If these credentials are correct, the application will store information about the authenticated user in the user's [session](/docs/{{version}}/session). A cookie issued to the browser contains the session ID so that subsequent requests to the application can associate the user with the correct session. After the session cookie is received, the application will retrieve the session data based on the session ID, note that the authentication information has been stored in the session, and will consider the user as "authenticated". -When a remote service needs to authenticate to access an API, cookies are not typically used because there is no web browser. Instead, the remote service sends an API token to the API on each request. The application may validate the incoming token against a table of valid API tokens and "authenticate" the request as being performed by the user associated with that API token. +When a remote service needs to authenticate to access an API, cookies are not typically used for authentication because there is no web browser. Instead, the remote service sends an API token to the API on each request. The application may validate the incoming token against a table of valid API tokens and "authenticate" the request as being performed by the user associated with that API token. <a name="laravels-built-in-browser-authentication-services"></a> #### Laravel's Built-in Browser Authentication Services -Laravel includes built-in authentication and session services which are typically accessed via the `Auth` and `Session` facades. These features provide cookie based authentication for requests that are initiated from web browsers. They provide methods that allow you to verify a user's credentials and authenticate the user. In addition, these services will automatically store the proper data in the user's session and issue the proper session cookie. A discussion of how to use these services is contained within this documentation. +Laravel includes built-in authentication and session services which are typically accessed via the `Auth` and `Session` facades. These features provide cookie based authentication for requests that are initiated from web browsers. They provide methods that allow you to verify a user's credentials and authenticate the user. In addition, these services will automatically store the proper authentication data in the user's session and issue the user's session cookie. A discussion of how to use these services is contained within this documentation. **Application Starter Kits** -As discussed in this documentation, you can interact with these authentication services manually to build your application's own authentication layer. However, to help you get started more quickly, we have released [free packages](/docs/{{version}}/starter-kits) that provide robust, modern scaffolding of the entire authentication layer. These packages are [Laravel Breeze](https://github.com/laravel/breeze), [Laravel Jetstream](https://jetstream.laravel.com), and [Laravel Fortify](https://github.com/laravel/fortify). +As discussed in this documentation, you can interact with these authentication services manually to build your application's own authentication layer. However, to help you get started more quickly, we have released [free packages](/docs/{{version}}/starter-kits) that provide robust, modern scaffolding of the entire authentication layer. These packages are [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze), [Laravel Jetstream](/docs/{{version}}/starter-kits#laravel-jetstream), and [Laravel Fortify](https://github.com/laravel/fortify). -Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). To get started, check out the documentation on Laravel's [application starter kits](/docs/{{version}}/starter-kits). +_Laravel Breeze_ is a simple, minimal implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is comprised of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). To get started, check out the documentation on Laravel's [application starter kits](/docs/{{version}}/starter-kits). -Laravel Fortify is a headless authentication backend for Laravel that implements many of the features found in this documentation, including cookie-based authentication as well as other features such as two-factor authentication and email verification. +_Laravel Fortify_ is a headless authentication backend for Laravel that implements many of the features found in this documentation, including cookie-based authentication as well as other features such as two-factor authentication and email verification. Fortify provides the authentication backend for Laravel Jetstream or may be used independently in combination with [Laravel Sanctum](/docs/{{version}}/sanctum) to provide authentication for an SPA that needs to authenticate with Laravel. -[Laravel Jetstream](https://jetstream.laravel.com) is an application starter kit that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream includes optional support for two-factor authentication, team support, browser session management, profile management, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below. +_[Laravel Jetstream](https://jetstream.laravel.com)_ is a robust application starter kit that consumes and exposes Laravel Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream includes optional support for two-factor authentication, team support, browser session management, profile management, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below. <a name="laravels-api-authentication-services"></a> #### Laravel's API Authentication Services @@ -87,7 +90,7 @@ Passport is an OAuth2 authentication provider, offering a variety of OAuth2 "gra **Sanctum** -In response to the complexity of OAuth2 and developer confusion, we set out to build a simpler, more streamlined authentication package that could handle both first-party web requests from a web browser and API requests via tokens. This goal was realized with the release of [Laravel Sanctum](/docs/{{version}}/sanctum), which should be considered the preferred and recommended authentication package for applications that will be offering a first-party web UI in addition to an API, or will be powered by a single-page application that exists separately from the backend Laravel application, or applications that offer a mobile client. +In response to the complexity of OAuth2 and developer confusion, we set out to build a simpler, more streamlined authentication package that could handle both first-party web requests from a web browser and API requests via tokens. This goal was realized with the release of [Laravel Sanctum](/docs/{{version}}/sanctum), which should be considered the preferred and recommended authentication package for applications that will be offering a first-party web UI in addition to an API, or will be powered by a single-page application (SPA) that exists separately from the backend Laravel application, or applications that offer a mobile client. Laravel Sanctum is a hybrid web / API authentication package that can manage your application's entire authentication process. This is possible because when Sanctum based applications receive a request, Sanctum will first determine if the request includes a session cookie that references an authenticated session. Sanctum accomplishes this by calling Laravel's built-in authentication services which we discussed earlier. If the request is not being authenticated via a session cookie, Sanctum will inspect the request for an API token. If an API token is present, Sanctum will authenticate the request using that token. To learn more about this process, please consult Sanctum's ["how it works"](/docs/{{version}}/sanctum#how-it-works) documentation. @@ -96,9 +99,11 @@ Laravel Sanctum is the API package we have chosen to include with the [Laravel J <a name="summary-choosing-your-stack"></a> #### Summary & Choosing Your Stack -In summary, if your application will be accessed using a browser, your application will use Laravel's built-in authentication services. +In summary, if your application will be accessed using a browser and you are building a monolithic Laravel application, your application will use Laravel's built-in authentication services. + +Next, if your application offers an API that will be consumed by third parties, you will choose between [Passport](/docs/{{version}}/passport) or [Sanctum](/docs/{{version}}/sanctum) to provide API token authentication for your application. In general, Sanctum should be preferred when possible since it is a simple, complete solution for API authentication, SPA authentication, and mobile authentication, including support for "scopes" or "abilities". -Next, if your application offers an API, you will choose between [Passport](/docs/{{version}}/passport) or [Sanctum](/docs/{{version}}/sanctum) to provide API token authentication for your application. In general, Sanctum should be preferred when possible since it is a simple, complete solution for API authentication, SPA authentication, and mobile authentication, including support for "scopes" or "abilities". +If you are building a single-page application (SPA) that will be powered by a Laravel backend. You should use [Laravel Sanctum](/docs/{{version}}/sanctum). When using Sanctum, you will either need to [manually implement your own backend authentication routes](#authenticating-users) or utilize [Laravel Fortify](https://github.com/laravel/fortify) as a headless authentication backend provider. Passport may be chosen when your application absolutely needs all of the features provided by the OAuth2 specification. @@ -118,29 +123,20 @@ Laravel Breeze is a minimal, simple implementation of all of Laravel's authentic [Laravel Jetstream](https://jetstream.laravel.com) is a more robust application starter kit that includes support for scaffolding your application with [Livewire](https://laravel-livewire.com) or [Inertia.js and Vue](https://inertiajs.com). In addition, Jetstream features optional support for two-factor authentication, teams, profile management, browser session management, API support via [Laravel Sanctum](/docs/{{version}}/sanctum), account deletion, and more. -<a name="path-customization"></a> -#### Path Customization - -When a user is successfully authenticated, they will typically be redirected to the `/home` URI. You can customize the post-authentication redirect path using the `HOME` constant defined in your `RouteServiceProvider`: - - public const HOME = '/home'; - -> {tip} When using Laravel Breeze or Laravel Jetstream, the installation process will change the value of the `HOME` constant from its default value of `/home` to `/dashboard`. - <a name="retrieving-the-authenticated-user"></a> ### Retrieving The Authenticated User -While handling an incoming request, you may access the authenticated user via the `Auth` facade: +After installing an authentication starter kit and allowing users to register and authenticate with your application, you will often need to interact with the currently authenticated user. While handling an incoming request, you may access the authenticated user via the `Auth` facade's `user` method: use Illuminate\Support\Facades\Auth; - // Get the currently authenticated user... + // Retrieve the currently authenticated user... $user = Auth::user(); - // Get the currently authenticated user's ID... + // Retrieve 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. By type-hinting the `Illuminate\Http\Request` object, you may gain convenient access to the authenticated user from any controller method in your application: +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. By type-hinting the `Illuminate\Http\Request` object, you may gain convenient access to the authenticated user from any controller method in your application via the request's `user` method: <?php @@ -151,21 +147,21 @@ Alternatively, once a user is authenticated, you may access the authenticated us class FlightController extends Controller { /** - * Get a list of all available flights. + * Update the flight information for an existing flight. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function update(Request $request) { - // $request->user() returns an instance of the authenticated user... + // $request->user() } } <a name="determining-if-the-current-user-is-authenticated"></a> #### Determining If The Current User Is Authenticated -To determine if the user is already logged into your application, you may use the `check` method on the `Auth` facade, which will return `true` if the user is authenticated: +To determine if the user making the incoming HTTP request is authenticated, you may use the `check` method on the `Auth` facade. This method will return `true` if the user is authenticated: use Illuminate\Support\Facades\Auth; @@ -178,16 +174,16 @@ To determine if the user is already logged into your application, you may use th <a name="protecting-routes"></a> ### Protecting Routes -[Route middleware](/docs/{{version}}/middleware) can be used to only allow authenticated users to access a given route. Laravel ships with an `auth` middleware, which references the `Illuminate\Auth\Middleware\Authenticate` class. Since this middleware is already registered in your HTTP kernel, all you need to do is attach the middleware to a route definition: +[Route middleware](/docs/{{version}}/middleware) can be used to only allow authenticated users to access a given route. Laravel ships with an `auth` middleware, which references the `Illuminate\Auth\Middleware\Authenticate` class. Since this middleware is already registered in your application's HTTP kernel, all you need to do is attach the middleware to a route definition: - Route::get('flights', function () { - // Only authenticated users may enter... + Route::get('/flights', function () { + // Only authenticated users may access this route... })->middleware('auth'); <a name="redirecting-unauthenticated-users"></a> #### Redirecting Unauthenticated Users -When the `auth` middleware detects an unauthorized user, it will redirect the user to the `login` [named route](/docs/{{version}}/routing#named-routes). You may modify this behavior by updating the `redirectTo` function in your `app/Http/Middleware/Authenticate.php` file: +When the `auth` middleware detects an unauthenticated user, it will redirect the user to the `login` [named route](/docs/{{version}}/routing#named-routes). You may modify this behavior by updating the `redirectTo` function in your application's `app/Http/Middleware/Authenticate.php` file: /** * Get the path the user should be redirected to. @@ -203,25 +199,25 @@ When the `auth` middleware detects an unauthorized user, it will redirect the us <a name="specifying-a-guard"></a> #### Specifying A Guard -When attaching the `auth` middleware to a route, you may also specify which guard should be used to authenticate the user. The guard specified should correspond to one of the keys in the `guards` array of your `auth.php` configuration file: +When attaching the `auth` middleware to a route, you may also specify which "guard" should be used to authenticate the user. The guard specified should correspond to one of the keys in the `guards` array of your `auth.php` configuration file: - Route::get('flights', function () { - // Only authenticated users may enter... - })->middleware('auth:api'); + Route::get('/flights', function () { + // Only authenticated users may access this route... + })->middleware('auth:admin'); <a name="login-throttling"></a> ### Login Throttling -If you are using Laravel Breeze or Laravel Jetstream, rate limiting will automatically be applied to login attempts. By default, the user will not be able to login for one minute if they fail to provide the correct credentials after several attempts. The throttling is unique to the user's username / e-mail address and their IP address. +If you are using the Laravel Breeze or Laravel Jetstream [starter kits](/docs/{{version}}/starter-kits), rate limiting will automatically be applied to login attempts. By default, the user will not be able to login for one minute if they fail to provide the correct credentials after several attempts. The throttling is unique to the user's username / email address and their IP address. -> {tip} If you would like to rate limit your own routes, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). +> {tip} If you would like to rate limit other routes in your application, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). <a name="authenticating-users"></a> ## Manually Authenticating Users -You are not required to use the authentication scaffolding included with the [application starter kits](/docs/{{version}}/starter-kits). If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch! +You are not required to use the authentication scaffolding included with Laravel's [application starter kits](/docs/{{version}}/starter-kits). If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch! -We will access Laravel's authentication services via the `Auth` [facade](/docs/{{version}}/facades), so we'll need to make sure to import the `Auth` facade at the top of the class. Next, let's check out the `attempt` method: +We will access Laravel's authentication services via the `Auth` [facade](/docs/{{version}}/facades), so we'll need to make sure to import the `Auth` facade at the top of the class. Next, let's check out the `attempt` method. The `attempt` method is normally used to handle authentication attempt's from your application's "login" form. If authentication is successful, you should regenerate the user's [session](/docs/{{version}}/session) to prevent [session fixation](https://en.wikipedia.org/wiki/Session_fixation): <?php @@ -229,6 +225,8 @@ We will access Laravel's authentication services via the `Auth` [facade](/docs/{ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; + use Illuminate\Support\Facades\Redirect; + use Illuminate\Validation\ValidationException; class LoginController extends Controller { @@ -236,54 +234,80 @@ We will access Laravel's authentication services via the `Auth` [facade](/docs/{ * Handle an authentication attempt. * * @param \Illuminate\Http\Request $request - * - * @return Response + * @return \Illuminate\Http\Response */ public function authenticate(Request $request) { $credentials = $request->only('email', 'password'); if (Auth::attempt($credentials)) { - // Authentication passed... - return redirect()->intended('dashboard'); + $request->session()->regenerate(); + + return Redirect::intended('dashboard'); } + + throw ValidationException::withMessages([ + 'email' => 'The provided credentials do not match our records.', + ]); } } -The `attempt` method accepts an array of key / value pairs as its first argument. The values in the array will be used to find the user in your database table. So, in the example above, the user will be retrieved by the value of the `email` column. If the user is found, the hashed password stored in the database will be compared with the `password` value passed to the method via the array. You should not hash the password specified as the `password` value, since the framework will automatically hash the value before comparing it to the hashed password in the database. If the two hashed passwords match an authenticated session will be started for the user. +The `attempt` method accepts an array of key / value pairs as its first argument. The values in the array will be used to find the user in your database table. So, in the example above, the user will be retrieved by the value of the `email` column. If the user is found, the hashed password stored in the database will be compared with the `password` value passed to the method via the array. You should not hash the incoming request's `password` value, since the framework will automatically hash the value before comparing it to the hashed password in the database. If the two hashed passwords match an authenticated session will be started for the user. + +Remember, Laravel's authentication services will retrieve users from your database based on your authentication guard's "provider" configuration. In the default `config/auth.php` configuration file, the Eloquent user provider is specified and it is instructed to use the `App\Models\User` model when retrieving users. You may change these values within your configuration file based on the needs of your application. The `attempt` method will return `true` if authentication was successful. Otherwise, `false` will be returned. -The `intended` method on the redirector will redirect the user to the URL they were attempting to access before being intercepted by the authentication middleware. A fallback URI may be given to this method in case the intended destination is not available. +The `intended` method on the `Redirect` facade will redirect the user to the URL they were attempting to access before being intercepted by the authentication middleware. A fallback URI may be given to this method in case the intended destination is not available. <a name="specifying-additional-conditions"></a> #### Specifying Additional Conditions -If you wish, you may also add extra conditions to the authentication query in addition to the user's e-mail and password. For example, we may verify that the user is marked as "active": +If you wish, you may also add extra query conditions to the authentication query in addition to the user's email and password. To accomplish this, we may simply add the query conditions to the array passed to the `attempt` method. For example, we may verify that the user is marked as "active": if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) { - // The user is active, not suspended, and exists. + // Authentication was successful... } -> {note} In these examples, `email` is not a required option, it is merely used as an example. You should use whatever column name corresponds to a "username" in your database. +> {note} In these examples, `email` is not a required option, it is merely used as an example. You should use whatever column name corresponds to a "username" in your database table. <a name="accessing-specific-guard-instances"></a> #### Accessing Specific Guard Instances -You may specify which guard instance you would like to utilize using the `guard` method on the `Auth` facade. This allows you to manage authentication for separate parts of your application using entirely separate authenticatable models or user tables. +Via the `Auth` facade's `guard` method, you may specify which guard instance you would like to utilize when authenticating the user. This allows you to manage authentication for separate parts of your application using entirely separate authenticatable models or user tables. The guard name passed to the `guard` method should correspond to one of the guards configured in your `auth.php` configuration file: if (Auth::guard('admin')->attempt($credentials)) { - // + // ... } <a name="manually-logging-out"></a> #### Logging Out -To log users out of your application, you may use the `logout` method on the `Auth` facade. This will clear the authentication information in the user's session: +To log users out of your application, you may use the `logout` method on the `Auth` facade. This will clear the authentication information in the user's session so that subsequent requests to the application are not authenticated. - Auth::logout(); +In addition to calling the `logout` method, it is recommended that you invalidate the user's session and regenerate their [CSRF token](/docs/{{version}}/csrf). After logging the user out, you would typically redirect the user to the root of your application: + + use Illuminate\Http\Request; + use Illuminate\Support\Facades\Auth; + + /** + * Log the user out of the application. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function logout(Request $request) + { + Auth::logout(); + + $request->session()->invalidate(); + + $request->session()->regenerateToken(); + + return redirect('/'); + } <a name="remembering-users"></a> ### Remembering Users diff --git a/sanctum.md b/sanctum.md index f28220f21a..adc1d71328 100644 --- a/sanctum.md +++ b/sanctum.md @@ -238,7 +238,7 @@ During this request Laravel will set an `XSRF-TOKEN` cookie containing the curre <a name="logging-in"></a> #### Logging In -Once CSRF protection has been initialized, you should make a `POST` request to the your Laravel application's `/login` route. This `/login` route may be [implemented manually](/docs/{{version}}/authentication#authenticating-users) or using a package like [Laravel Fortify](https://github.com/laravel/fortify). +Once CSRF protection has been initialized, you should make a `POST` request to the your Laravel application's `/login` route. This `/login` route may be [implemented manually](/docs/{{version}}/authentication#authenticating-users) or using a headless authentication package like [Laravel Fortify](https://github.com/laravel/fortify). If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client. In addition, since your application already made a request to the `/sanctum/csrf-cookie` route, subsequent requests should automatically receive CSRF protection as long as your JavaScript HTTP client sends the value of `XSRF-TOKEN` cookie in the `X-XSRF-TOKEN` header. From 34f44c1b662eb3ad755e8c40d28bd77f6e345488 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 13:29:19 -0600 Subject: [PATCH 135/274] wip --- authentication.md | 78 +++++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/authentication.md b/authentication.md index a35467edb7..680707fe49 100644 --- a/authentication.md +++ b/authentication.md @@ -312,16 +312,14 @@ In addition to calling the `logout` method, it is recommended that you invalidat <a name="remembering-users"></a> ### Remembering Users -If you would like to provide "remember me" functionality in your application, you may pass a boolean value as the second argument to the `attempt` method, which will keep the user authenticated indefinitely, or until they manually logout. Your `users` table must include the string `remember_token` column, which will be used to store the "remember me" token. +Many web applications provide a "remember me" checkbox on their login form. If you would like to provide "remember me" functionality in your application, you may pass a boolean value as the second argument to the `attempt` method. - if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { - // The user is being remembered... - } +When this value is `true`, Laravel will keep the user authenticated indefinitely or until they manually logout. Your `users` table must include the string `remember_token` column, which will be used to store the "remember me" token. The `users` table migration included with new Laravel applications already includes this column: -If you are "remembering" users, you may use the `viaRemember` method to determine if the user was authenticated using the "remember me" cookie: + use Illuminate\Support\Facades\Auth; - if (Auth::viaRemember()) { - // + if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) { + // The user is being remembered... } <a name="other-authentication-methods"></a> @@ -330,31 +328,35 @@ If you are "remembering" users, you may use the `viaRemember` method to determin <a name="authenticate-a-user-instance"></a> #### Authenticate A User Instance -If you need to log an existing user instance into your application, you may call the `login` method with the user instance. The given object must be an implementation of the `Illuminate\Contracts\Auth\Authenticatable` [contract](/docs/{{version}}/contracts). The `App\Models\User` model included with Laravel already implements this interface. This method of authentication is useful when you already have a valid user instance, such as directly after a user registers with your application: +If you need to set an existing user instance as the currently authenticated user, you may pass the user instance to the `Auth` facade's `login` method. The given user instance must be an implementation of the `Illuminate\Contracts\Auth\Authenticatable` [contract](/docs/{{version}}/contracts). The `App\Models\User` model included with Laravel already implements this interface. This method of authentication is useful when you already have a valid user instance, such as directly after a user registers with your application: + + use Illuminate\Support\Facades\Auth; Auth::login($user); - // Login and "remember" the given user... - Auth::login($user, true); +You may pass a boolean value as the second argument to the `login` method. This value indicates if "remember me" functionality is desired for the authenticated session. Remember, this means that the session will be authenticated indefinitely or until the user manually logs out of the application: -You may specify the guard instance you would like to use: + Auth::login($user, $remember = true); + +If needed, you may specify an authentication guard before calling the `login` method: Auth::guard('admin')->login($user); <a name="authenticate-a-user-by-id"></a> #### Authenticate A User By ID -To log a user into the application by their ID, you may use the `loginUsingId` method. This method accepts the primary key of the user you wish to authenticate: +To authenticate a user using their database record's primary key, you may use the `loginUsingId` method. This method accepts the primary key of the user you wish to authenticate: Auth::loginUsingId(1); - // Login and "remember" the given user... - Auth::loginUsingId(1, true); +You may pass a boolean value as the second argument to the `loginUsingId` method. This value indicates if "remember me" functionality is desired for the authenticated session. Remember, this means that the session will be authenticated indefinitely or until the user manually logs out of the application: + + Auth::loginUsingId(1, $remember = true); <a name="authenticate-a-user-once"></a> #### 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: +You may use the `once` method to authenticate a user with the application for a single request. No sessions or cookies will be utilized when calling this method: if (Auth::once($credentials)) { // @@ -363,18 +365,18 @@ You may use the `once` method to log a user into the application for a single re <a name="http-basic-authentication"></a> ## HTTP Basic Authentication -[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: +[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 a route. The `auth.basic` middleware is included with the Laravel framework, so you do not need to define it: - Route::get('profile', function () { - // Only authenticated users may enter... + Route::get('/profile', function () { + // Only authenticated users may access this route... })->middleware('auth.basic'); -Once the middleware has been attached to the route, you will automatically be prompted for credentials when accessing the route in your browser. By default, the `auth.basic` middleware will use the `email` column on the user record as the "username". +Once the middleware has been attached to the route, you will automatically be prompted for credentials when accessing the route in your browser. By default, the `auth.basic` middleware will assume the `email` column on your `users` database table is the user's "username". <a name="a-note-on-fastcgi"></a> #### A Note On FastCGI -If you are using PHP FastCGI, HTTP Basic authentication may not work correctly out of the box. The following lines should be added to your `.htaccess` file: +If you are using PHP FastCGI and Apache to serve your Laravel application, HTTP Basic authentication may not work correctly. To correct these problems, the following lines may be added to your application's `.htaccess` file: RewriteCond %{HTTP:Authorization} ^(.+)$ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] @@ -382,7 +384,7 @@ If you are using PHP FastCGI, HTTP Basic authentication may not work correctly o <a name="stateless-http-basic-authentication"></a> ### Stateless HTTP Basic Authentication -You may also use HTTP Basic Authentication without setting a user identifier cookie in the session, which is particularly useful for API authentication. To do so, [define a middleware](/docs/{{version}}/middleware) that calls the `onceBasic` method. If no response is returned by the `onceBasic` method, the request may be passed further into the application: +You may also use HTTP Basic Authentication without setting a user identifier cookie in the session. This is primarily helpful if you choose to use HTTP Authentication to authenticate requests to your application's API. To accomplish this, [define a middleware](/docs/{{version}}/middleware) that calls the `onceBasic` method. If no response is returned by the `onceBasic` method, the request may be passed further into the application: <?php @@ -408,25 +410,43 @@ 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 () { - // Only authenticated users may enter... + Route::get('/api/user', function () { + // Only authenticated users may access this route... })->middleware('auth.basic.once'); <a name="logging-out"></a> ## Logging Out -To manually log users out of your application, you may use the `logout` method on the `Auth` facade. This will clear the authentication information in the user's session: +To manually log users out of your application, you may use the `logout` method provided by the `Auth` facade. This will remove the authentication information from the user's session so that subsequent requests are not authenticated. +In addition to calling the `logout` method, it is recommended that you invalidate the user's session and regenerate their [CSRF token](/docs/{{version}}/csrf). After logging the user out, you would typically redirect the user to the root of your application: + + use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; - Auth::logout(); + /** + * Log the user out of the application. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function logout(Request $request) + { + Auth::logout(); + + $request->session()->invalidate(); + + $request->session()->regenerateToken(); + + return redirect('/'); + } <a name="invalidating-sessions-on-other-devices"></a> ### Invalidating Sessions On Other Devices Laravel also provides a mechanism for invalidating and "logging out" a user's sessions that are active on other devices without invalidating the session on their current device. This feature is typically utilized when a user is changing or updating their password and you would like to invalidate sessions on other devices while keeping the current device authenticated. -Before getting started, you should make sure that the `Illuminate\Session\Middleware\AuthenticateSession` middleware is present and un-commented in your `app/Http/Kernel.php` class' `web` middleware group: +Before getting started, you should make sure that the `Illuminate\Session\Middleware\AuthenticateSession` middleware is present and un-commented in your `App\Http\Kernel` class' `web` middleware group: 'web' => [ // ... @@ -434,16 +454,14 @@ Before getting started, you should make sure that the `Illuminate\Session\Middle // ... ], -Then, you may use the `logoutOtherDevices` method on the `Auth` facade. This method requires the user to provide their current password, which your application should accept through an input form: +Then, you may use the `logoutOtherDevices` method provided by the `Auth` facade. This method requires the user to confirm their current password, which your application should accept through an input form: use Illuminate\Support\Facades\Auth; - Auth::logoutOtherDevices($password); + Auth::logoutOtherDevices($currentPassword); When the `logoutOtherDevices` method is invoked, the user's other sessions will be invalidated entirely, meaning they will be "logged out" of all guards they were previously authenticated by. -> {note} When using the `AuthenticateSession` middleware in combination with a custom route name for the `login` route, you must override the `unauthenticated` method on your application's exception handler to properly redirect users to your login page. - <a name="password-confirmation"></a> ## Password Confirmation From ab8ecac2492dd3328515c7dd11c91d897e158662 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 13:49:21 -0600 Subject: [PATCH 136/274] wip --- authentication.md | 59 ++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/authentication.md b/authentication.md index 680707fe49..38980af113 100644 --- a/authentication.md +++ b/authentication.md @@ -225,8 +225,6 @@ We will access Laravel's authentication services via the `Auth` [facade](/docs/{ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; - use Illuminate\Support\Facades\Redirect; - use Illuminate\Validation\ValidationException; class LoginController extends Controller { @@ -243,10 +241,10 @@ We will access Laravel's authentication services via the `Auth` [facade](/docs/{ if (Auth::attempt($credentials)) { $request->session()->regenerate(); - return Redirect::intended('dashboard'); + return redirect()->intended('dashboard'); } - throw ValidationException::withMessages([ + return back()->withErrors([ 'email' => 'The provided credentials do not match our records.', ]); } @@ -258,7 +256,7 @@ Remember, Laravel's authentication services will retrieve users from your databa The `attempt` method will return `true` if authentication was successful. Otherwise, `false` will be returned. -The `intended` method on the `Redirect` facade will redirect the user to the URL they were attempting to access before being intercepted by the authentication middleware. A fallback URI may be given to this method in case the intended destination is not available. +The `intended` method provided by Laravel's redirector will redirect the user to the URL they were attempting to access before being intercepted by the authentication middleware. A fallback URI may be given to this method in case the intended destination is not available. <a name="specifying-additional-conditions"></a> #### Specifying Additional Conditions @@ -465,14 +463,14 @@ When the `logoutOtherDevices` method is invoked, the user's other sessions will <a name="password-confirmation"></a> ## Password Confirmation -While building your application, you may occasionally have actions that should require the user to confirm their password before the action is performed. Laravel includes built-in middleware to make this process a breeze. Implementing this feature will require you to define two routes: one route to display a view asking the user to confirm their password, and one route to confirm that the password is valid and redirect the user to their intended destination. +While building your application, you may occasionally have actions that should require the user to confirm their password before the action is performed or before the user is redirected to a sensitive area of the application. Laravel includes built-in middleware to make this process a breeze. Implementing this feature will require you to define two routes: one route to display a view asking the user to confirm their password and another route to confirm that the password is valid and redirect the user to their intended destination. > {tip} The following documentation discusses how to integrate with Laravel's password confirmation features directly; however, if you would like to get started more quickly, the [Laravel application starter kits](/docs/{{version}}/starter-kits) include support for this feature! <a name="password-confirmation-configuration"></a> ### Configuration -After confirming their password, a user will not be asked to confirm their password again for three hours. However, you may configure the length of time before the user is re-prompted for their password by changing the value of the `password_timeout` configuration value within your `auth` configuration file. +After confirming their password, a user will not be asked to confirm their password again for three hours. However, you may configure the length of time before the user is re-prompted for their password by changing the value of the `password_timeout` configuration value within your application's `config/auth.php` configuration file. <a name="password-confirmation-routing"></a> ### Routing @@ -480,11 +478,11 @@ After confirming their password, a user will not be asked to confirm their passw <a name="the-password-confirmation-form"></a> #### The Password Confirmation Form -First, we will define the route that is needed to display a view requesting that the user confirm their password: +First, we will define a route to display a view that requests that the user confirm their password: Route::get('/confirm-password', function () { return view('auth.confirm-password'); - })->middleware(['auth'])->name('password.confirm'); + })->middleware('auth')->name('password.confirm'); As you might expect, the view that is returned by this route should have a form containing a `password` field. In addition, feel free to include text within the view that explains that the user is entering a protected area of the application and must confirm their password. @@ -495,6 +493,7 @@ Next, we will define a route that will handle the form request from the "confirm use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; + use Illuminate\Support\Facades\Redirect; Route::post('/confirm-password', function (Request $request) { if (! Hash::check($request->password, $request->user()->password)) { @@ -508,12 +507,12 @@ Next, we will define a route that will handle the form request from the "confirm return redirect()->intended(); })->middleware(['auth', 'throttle:6,1'])->name('password.confirm'); -Before moving on, let's examine this route in more detail. First, the request's `password` attribute is determined to actually match the authenticated user's password. If the password is valid, we need to inform Laravel's session that the user has confirmed their password. The `passwordConfirmed` method will set a timestamp in the user's session that Laravel can use to determine when the user last confirmed their password. Finally, we can redirect the user to their intended destination. +Before moving on, let's examine this route in more detail. First, the request's `password` field is determined to actually match the authenticated user's password. If the password is valid, we need to inform Laravel's session that the user has confirmed their password. The `passwordConfirmed` method will set a timestamp in the user's session that Laravel can use to determine when the user last confirmed their password. Finally, we can redirect the user to their intended destination. <a name="password-confirmation-protecting-routes"></a> ### Protecting Routes -You should ensure that any route that performs an action that should require recent password confirmation is assigned the `password.confirm` middleware. This middleware is included with the default installation of Laravel and will automatically store the user's intended destination in the session so that the user may be redirected to that location after confirming their password. After storing the user's intended destination in the session, the middleware will redirect the user to the `password.confirm` [named route](/docs/{{version}}/routing#named-routes): +You should ensure that any route that performs an action which requires recent password confirmation is assigned the `password.confirm` middleware. This middleware is included with the default installation of Laravel and will automatically store the user's intended destination in the session so that the user may be redirected to that location after confirming their password. After storing the user's intended destination in the session, the middleware will redirect the user to the `password.confirm` [named route](/docs/{{version}}/routing#named-routes): Route::get('/settings', function () { // ... @@ -526,7 +525,7 @@ You should ensure that any route that performs an action that should require rec <a name="adding-custom-guards"></a> ## Adding Custom Guards -You may define your own authentication guards using the `extend` method on the `Auth` facade. You should place this call to `extend` within a [service provider](/docs/{{version}}/providers). Since Laravel already ships with an `AuthServiceProvider`, we can place the code in that provider: +You may define your own authentication guards using the `extend` method on the `Auth` facade. You should place your call to the `extend` method within a [service provider](/docs/{{version}}/providers). Since Laravel already ships with an `AuthServiceProvider`, we can place the code in that provider: <?php @@ -555,7 +554,7 @@ You may define your own authentication guards using the `extend` method on the ` } } -As you can see in the example above, the callback passed to the `extend` method should return an implementation of `Illuminate\Contracts\Auth\Guard`. This interface contains a few methods you will need to implement to define a custom guard. Once your custom guard has been defined, you may use this guard in the `guards` configuration of your `auth.php` configuration file: +As you can see in the example above, the callback passed to the `extend` method should return an implementation of `Illuminate\Contracts\Auth\Guard`. This interface contains a few methods you will need to implement to define a custom guard. Once your custom guard has been defined, you may reference the guard in the `guards` configuration of your `auth.php` configuration file: 'guards' => [ 'api' => [ @@ -584,12 +583,12 @@ To get started, call the `Auth::viaRequest` method within the `boot` method of y { $this->registerPolicies(); - Auth::viaRequest('custom-token', function ($request) { + Auth::viaRequest('custom-token', function (Request $request) { return User::where('token', $request->token)->first(); }); } -Once your custom authentication driver has been defined, you use it as a driver within the `guards` configuration of your `auth.php` configuration file: +Once your custom authentication driver has been defined, you may configure it as a driver within the `guards` configuration of your `auth.php` configuration file: 'guards' => [ 'api' => [ @@ -600,13 +599,13 @@ Once your custom authentication driver has been defined, you use it as a driver <a name="adding-custom-user-providers"></a> ## Adding Custom User Providers -If you are not using a traditional relational database to store your users, you will need to extend Laravel with your own authentication user provider. We will use the `provider` method on the `Auth` facade to define a custom user provider: +If you are not using a traditional relational database to store your users, you will need to extend Laravel with your own authentication user provider. We will use the `provider` method on the `Auth` facade to define a custom user provider. The user provider resolver should return an implementation of `Illuminate\Contracts\Auth\UserProvider`: <?php namespace App\Providers; - use App\Extensions\RiakUserProvider; + use App\Extensions\MongoUserProvider; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Auth; @@ -621,10 +620,10 @@ 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('mongo', function ($app, array $config) { // Return an instance of Illuminate\Contracts\Auth\UserProvider... - return new RiakUserProvider($app->make('riak.connection')); + return new MongoUserProvider($app->make('mongo.connection')); }); } } @@ -633,11 +632,11 @@ After you have registered the provider using the `provider` method, you may swit 'providers' => [ 'users' => [ - 'driver' => 'riak', + 'driver' => 'mongo', ], ], -Finally, you may use this provider in your `guards` configuration: +Finally, you may reference this provider in your `guards` configuration: 'guards' => [ 'web' => [ @@ -649,7 +648,7 @@ Finally, you may use this provider in your `guards` configuration: <a name="the-user-provider-contract"></a> ### The User Provider Contract -The `Illuminate\Contracts\Auth\UserProvider` implementations are only responsible for fetching an `Illuminate\Contracts\Auth\Authenticatable` implementation out of a persistent storage system, such as MySQL, Riak, etc. These two interfaces allow the Laravel authentication mechanisms to continue functioning regardless of how the user data is stored or what type of class is used to represent it. +`Illuminate\Contracts\Auth\UserProvider` implementations are responsible for fetching an `Illuminate\Contracts\Auth\Authenticatable` implementation out of a persistent storage system, such as MySQL, MongoDB, etc. These two interfaces allow the Laravel authentication mechanisms to continue functioning regardless of how the user data is stored or what type of class is used to represent the authenticated user: Let's take a look at the `Illuminate\Contracts\Auth\UserProvider` contract: @@ -668,18 +667,18 @@ Let's take a look at the `Illuminate\Contracts\Auth\UserProvider` contract: The `retrieveById` function typically receives a key representing the user, such as an auto-incrementing ID from a MySQL database. The `Authenticatable` implementation matching the ID should be retrieved and returned by the method. -The `retrieveByToken` function retrieves a user by their unique `$identifier` and "remember me" `$token`, stored in a field `remember_token`. As with the previous method, the `Authenticatable` implementation should be returned. +The `retrieveByToken` function retrieves a user by their unique `$identifier` and "remember me" `$token`, typically stored in a database column like `remember_token`. As with the previous method, the `Authenticatable` implementation with a matching token value should be returned by this method. -The `updateRememberToken` method updates the `$user` field `remember_token` with the new `$token`. A fresh token is assigned on a successful "remember me" login attempt or when the user is logging out. +The `updateRememberToken` method updates the `$user` instance's `remember_token` with the new `$token`. A fresh token is assigned to users on a successful "remember me" authentication attempt or when the user is logging out. -The `retrieveByCredentials` method receives the array of credentials passed to the `Auth::attempt` method when attempting to sign into an application. The method should then "query" the underlying persistent storage for the user matching those credentials. Typically, this method will run a query with a "where" condition on `$credentials['username']`. The method should then return an implementation of `Authenticatable`. **This method should not attempt to do any password validation or authentication.** +The `retrieveByCredentials` method receives the array of credentials passed to the `Auth::attempt` method when attempting to authenticate with an application. The method should then "query" the underlying persistent storage for the user matching those credentials. Typically, this method will run a query with a "where" condition that searches for a user record with a "username" matching the value of `$credentials['username']`. The method should return an implementation of `Authenticatable`. **This method should not attempt to do any password validation or authentication.** -The `validateCredentials` method should compare the given `$user` with the `$credentials` to authenticate the user. For example, this method should probably use `Hash::check` to compare the value of `$user->getAuthPassword()` to the value of `$credentials['password']`. This method should return `true` or `false` indicating on whether the password is valid. +The `validateCredentials` method should compare the given `$user` with the `$credentials` to authenticate the user. For example, this method will typically use the `Hash::check` method to compare the value of `$user->getAuthPassword()` to the value of `$credentials['password']`. This method should return `true` or `false` indicating whether the password is valid. <a name="the-authenticatable-contract"></a> ### The Authenticatable Contract -Now that we have explored each of the methods on the `UserProvider`, let's take a look at the `Authenticatable` contract. Remember, the provider should return implementations of this interface from the `retrieveById`, `retrieveByToken`, and `retrieveByCredentials` methods: +Now that we have explored each of the methods on the `UserProvider`, let's take a look at the `Authenticatable` contract. Remember, user providers should return implementations of this interface from the `retrieveById`, `retrieveByToken`, and `retrieveByCredentials` methods: <?php @@ -695,12 +694,14 @@ Now that we have explored each of the methods on the `UserProvider`, let's take public function getRememberTokenName(); } -This interface is simple. The `getAuthIdentifierName` method should return the name of the "primary key" field of the user and the `getAuthIdentifier` method should return the "primary key" of the user. In a MySQL back-end, again, this would be the auto-incrementing primary key. The `getAuthPassword` should return the user's hashed password. This interface allows the authentication system to work with any User class, regardless of what ORM or storage abstraction layer you are using. By default, Laravel includes a `User` class in the `app/Models` directory which implements this interface, so you may consult this class for an implementation example. +This interface is simple. The `getAuthIdentifierName` method should return the name of the "primary key" field of the user and the `getAuthIdentifier` method should return the "primary key" of the user. When using a MySQL back-end, this would likely be the auto-incrementing primary key assigned to the user record. The `getAuthPassword` method should return the user's hashed password. + +This interface allows the authentication system to work with any "user" class, regardless of what ORM or storage abstraction layer you are using. By default, Laravel includes a `App\Models\User` class in the `app/Models` directory which implements this interface. <a name="events"></a> ## Events -Laravel raises a variety of [events](/docs/{{version}}/events) during the authentication process. You may attach listeners to these events in your `EventServiceProvider`: +Laravel dispatches a variety of [events](/docs/{{version}}/events) during the authentication process. You may attach listeners to these events in your `EventServiceProvider`: /** * The event listener mappings for the application. From 2fd34836259d12f0d157589f2ab65b652438ec86 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 14:29:02 -0600 Subject: [PATCH 137/274] wip --- authorization.md | 106 ++++++++++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/authorization.md b/authorization.md index be3c9833c9..c71785bb46 100644 --- a/authorization.md +++ b/authorization.md @@ -25,9 +25,9 @@ <a name="introduction"></a> ## Introduction -In addition to providing [authentication](/docs/{{version}}/authentication) services out of the box, Laravel also provides a simple way to authorize user actions against a given resource. Like authentication, Laravel's approach to authorization is simple, and there are two primary ways of authorizing actions: gates and policies. +In addition to providing built-in [authentication](/docs/{{version}}/authentication) services, Laravel also provides a simple way to authorize user actions against a given resource. For example, even though a user is authenticated, they may not be authorized to update or delete certain Eloquent models or database records managed by your application. -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. +Laravel provides two primary ways of authorizing actions: gates and policies. Think of gates and policies like routes and controllers. Gates provide a simple, closure based approach to authorization while policies, like controllers, group logic around a particular model or resource. In this documentation, we'll explore gates first and then examine policies. 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. @@ -37,7 +37,13 @@ You do not need to choose between exclusively using gates or exclusively using p <a name="writing-gates"></a> ### 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: +Gates are closures that determine if a user is authorized to perform a given action. Typically, gates are defined within the `boot` method of 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. + +In this example, we'll define a gate to determine if a user can update a given `App\Models\Post` model. The gate will accomplish this may comparing the user's `id` against the `user_id` of the user that created the post: + + use App\Models\Post; + use App\Models\User; + use Illuminate\Support\Facades\Gate; /** * Register any authentication / authorization services. @@ -48,18 +54,15 @@ Gates are closures that determine if a user is authorized to perform a given act { $this->registerPolicies(); - Gate::define('edit-settings', function ($user) { - return $user->isAdmin; - }); - - Gate::define('update-post', function ($user, $post) { + Gate::define('update-post', function (User $user, Post $post) { return $user->id === $post->user_id; }); } -Gates may also be defined using a class callback array, like controllers: +Like controllers, gates may also be defined using a class callback array: use App\Policies\PostPolicy; + use Illuminate\Support\Facades\Gate; /** * Register any authentication / authorization services. @@ -76,21 +79,37 @@ Gates may also be defined using a class callback array, like controllers: <a name="authorizing-actions-via-gates"></a> ### Authorizing Actions -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: +To authorize an action using gates, you should use the `allows` or `denies` methods provided by the `Gate` facade. 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. It is typical to call the gate authorization methods within your application's controllers before performing an action that requires authorization: - if (Gate::allows('edit-settings')) { - // The current user can edit settings - } + <?php - if (Gate::allows('update-post', $post)) { - // The current user can update the post... - } + namespace App\Http\Controllers; + + use App\Http\Controllers\Controller; + use App\Models\Post; + use Illuminate\Http\Request; + use Illuminate\Support\Facades\Gate; + + class PostController extends Controller + { + /** + * Update the given post. + * + * @param \Illuminate\Http\Request $request + * @param \App\Models\Post $post + * @return \Illuminate\Http\Response + */ + public function update(Request $request, Post $post) + { + if (! Gate::allows('update-post', $post)) { + abort(403); + } - if (Gate::denies('update-post', $post)) { - // The current user can't update the post... + // 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: +If you would like to determine if a user other than the currently authenticated user is authorized to perform an action, you may use the `forUser` method on the `Gate` facade: if (Gate::forUser($user)->allows('update-post', $post)) { // The user can update the post... @@ -100,20 +119,20 @@ If you would like to determine if a particular user is authorized to perform an // The user can't update the post... } -You may authorize multiple actions at a time with the `any` or `none` methods: +You may authorize multiple actions at a time using the `any` or `none` methods: if (Gate::any(['update-post', 'delete-post'], $post)) { - // The user can update or delete the post + // The user can update or delete the post... } if (Gate::none(['update-post', 'delete-post'], $post)) { - // The user cannot update or delete the post + // The user can't update or delete the post... } <a name="authorizing-or-throwing-exceptions"></a> #### Authorizing Or Throwing Exceptions -If you would like to attempt to authorize an action and automatically throw an `Illuminate\Auth\Access\AuthorizationException` if the user is not allowed to perform the given action, you may use the `Gate::authorize` method. Instances of `AuthorizationException` are automatically converted to `403` HTTP response: +If you would like to attempt to authorize an action and automatically throw an `Illuminate\Auth\Access\AuthorizationException` if the user is not allowed to perform the given action, you may use the `Gate` facade's `authorize` method. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler: Gate::authorize('update-post', $post); @@ -122,13 +141,23 @@ If you would like to attempt to authorize an action and automatically throw an ` <a name="gates-supplying-additional-context"></a> #### Supplying Additional Context -The gate methods for authorizing abilities (`allows`, `denies`, `check`, `any`, `none`, `authorize`, `can`, `cannot`) and the authorization [Blade directives](#via-blade-templates) (`@can`, `@cannot`, `@canany`) can receive an array as the second argument. These array elements are passed as parameters to gate, and can be used for additional context when making authorization decisions: +The gate methods for authorizing abilities (`allows`, `denies`, `check`, `any`, `none`, `authorize`, `can`, `cannot`) and the authorization [Blade directives](#via-blade-templates) (`@can`, `@cannot`, `@canany`) can receive an array as their second argument. These array elements are passed as parameters to the gate closure, and can be used for additional context when making authorization decisions: - Gate::define('create-post', function ($user, $category, $extraFlag) { - return $category->group > 3 && $extraFlag === true; + use App\Models\Category; + use App\Models\User; + use Illuminate\Support\Facades\Gate; + + Gate::define('create-post', function (User $user, Category $category, $pinned) { + if (! $user->canPublishToGroup($category->group)) { + return false; + } elseif ($pinned && ! $user->canPinPosts()) { + return false; + } + + return true; }); - if (Gate::check('create-post', [$category, $extraFlag])) { + if (Gate::check('create-post', [$category, $pinned])) { // The user can create the post... } @@ -137,18 +166,19 @@ The gate methods for authorizing abilities (`allows`, `denies`, `check`, `any`, So far, we have only examined gates that return simple boolean values. However, sometimes you may wish to return a more detailed response, including an error message. To do so, you may return an `Illuminate\Auth\Access\Response` from your gate: + use App\Models\User; use Illuminate\Auth\Access\Response; use Illuminate\Support\Facades\Gate; - Gate::define('edit-settings', function ($user) { + Gate::define('edit-settings', function (User $user) { return $user->isAdmin ? Response::allow() - : Response::deny('You must be a super administrator.'); + : Response::deny('You must be an administrator.'); }); -When returning an authorization response from your gate, the `Gate::allows` method will still return a simple boolean value; however, you may use the `Gate::inspect` method to get the full authorization response returned by the gate: +Even when you return an authorization response from your gate, the `Gate::allows` method will still return a simple boolean value; however, you may use the `Gate::inspect` method to get the full authorization response returned by the gate: - $response = Gate::inspect('edit-settings', $post); + $response = Gate::inspect('edit-settings'); if ($response->allowed()) { // The action is authorized... @@ -156,16 +186,18 @@ When returning an authorization response from your gate, the `Gate::allows` meth echo $response->message(); } -Of course, when using the `Gate::authorize` method to throw an `AuthorizationException` if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response: +When using the `Gate::authorize` method, which throws an `AuthorizationException` if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response: - Gate::authorize('edit-settings', $post); + Gate::authorize('edit-settings'); // The action is authorized... <a name="intercepting-gate-checks"></a> ### Intercepting Gate Checks -Sometimes, you may wish to grant all abilities to a specific user. You may use the `before` method to define a callback that is run before all other authorization checks: +Sometimes, you may wish to grant all abilities to a specific user. You may use the `before` method to define a closure that is run before all other authorization checks: + + use Illuminate\Support\Facades\Gate; Gate::before(function ($user, $ability) { if ($user->isSuperAdmin()) { @@ -173,9 +205,9 @@ Sometimes, you may wish to grant all abilities to a specific user. You may use t } }); -If the `before` callback returns a non-null result that result will be considered the result of the check. +If the `before` closure returns a non-null result that result will be considered the result of the authorization check. -You may use the `after` method to define a callback to be executed after all other authorization checks: +You may use the `after` method to define a closure to be executed after all other authorization checks: Gate::after(function ($user, $ability, $result, $arguments) { if ($user->isSuperAdmin()) { @@ -183,7 +215,7 @@ You may use the `after` method to define a callback to be executed after all oth } }); -Similar to the `before` check, if the `after` callback returns a non-null result that result will be considered the result of the check. +Similar to the `before` method, if the `after` closure returns a non-null result that result will be considered the result of the authorization check. <a name="creating-policies"></a> ## Creating Policies From a483d684f87bf290892025a77945dc63e97a301d Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 15:13:58 -0600 Subject: [PATCH 138/274] wip --- authorization.md | 233 +++++++++++++++++++++++++++++++---------------- 1 file changed, 155 insertions(+), 78 deletions(-) diff --git a/authorization.md b/authorization.md index c71785bb46..a372aa84e2 100644 --- a/authorization.md +++ b/authorization.md @@ -200,7 +200,7 @@ Sometimes, you may wish to grant all abilities to a specific user. You may use t use Illuminate\Support\Facades\Gate; Gate::before(function ($user, $ability) { - if ($user->isSuperAdmin()) { + if ($user->isAdministrator()) { return true; } }); @@ -210,7 +210,7 @@ If the `before` closure returns a non-null result that result will be considered You may use the `after` method to define a closure to be executed after all other authorization checks: Gate::after(function ($user, $ability, $result, $arguments) { - if ($user->isSuperAdmin()) { + if ($user->isAdministrator()) { return true; } }); @@ -223,22 +223,22 @@ Similar to the `before` method, if the `after` closure returns a non-null result <a name="generating-policies"></a> ### Generating Policies -Policies are classes that organize authorization logic around a particular model or resource. For example, if your application is a blog, you may have a `Post` model and a corresponding `PostPolicy` to authorize user actions such as creating or updating posts. +Policies are classes that organize authorization logic around a particular model or resource. For example, if your application is a blog, you may have a `App\Models\Post` model and a corresponding `App\Policies\PostPolicy` to authorize user actions such as creating or updating posts. -You may generate a policy using the `make:policy` [artisan command](/docs/{{version}}/artisan). The generated policy will be placed in the `app/Policies` directory. If this directory does not exist in your application, Laravel will create it for you: +You may generate a policy using the `make:policy` Artisan command. The generated policy will be placed in the `app/Policies` directory. If this directory does not exist in your application, Laravel will create it for you: php artisan make:policy PostPolicy -The `make:policy` command will generate an empty policy class. If you would like to generate a class with the basic "CRUD" policy methods already included in the class, you may specify a `--model` when executing the command: +The `make:policy` command will generate an empty policy class. If you would like to generate a class with example policy methods related to viewing, creating, updating, and deleting the resource, you may provide a `--model` option when executing the command: php artisan make:policy PostPolicy --model=Post -> {tip} All policies are resolved via the Laravel [service container](/docs/{{version}}/container), allowing you to type-hint any needed dependencies in the policy's constructor to have them automatically injected. - <a name="registering-policies"></a> ### Registering Policies -Once the policy exists, it needs to be registered. The `AuthServiceProvider` included with fresh Laravel applications contains a `policies` property which maps your Eloquent models to their corresponding policies. Registering a policy will instruct Laravel which policy to utilize when authorizing actions against a given model: +Once the policy class has been created, it needs to be registered. Registering policies is how we can inform Laravel which policy to use when authorizing actions against a given model type. + +The `App\Providers\AuthServiceProvider` included with fresh Laravel applications contains a `policies` property which maps your Eloquent models to their corresponding policies. Registering a policy will instruct Laravel which policy to utilize when authorizing actions against a given Eloquent model: <?php @@ -276,14 +276,14 @@ Once the policy exists, it needs to be registered. The `AuthServiceProvider` inc <a name="policy-auto-discovery"></a> #### Policy Auto-Discovery -Instead of manually registering model policies, Laravel can auto-discover policies as long as the model and policy follow standard Laravel naming conventions. Specifically, the policies must be in a `Policies` directory at or above the directory that contains your models. So, for example, the models may be placed in the `app/Models` directory while the policies may be placed in the `app/Policies` directory. In this situation, Laravel will check for policies in `app/Models/Policies` then `app/Policies`. In addition, the policy name must match the model name and have a `Policy` suffix. So, a `User` model would correspond to a `UserPolicy` class. +Instead of manually registering model policies, Laravel can automatically discover policies as long as the model and policy follow standard Laravel naming conventions. Specifically, the policies must be in a `Policies` directory at or above the directory that contains your models. So, for example, the models may be placed in the `app/Models` directory while the policies may be placed in the `app/Policies` directory. In this situation, Laravel will check for policies in `app/Models/Policies` then `app/Policies`. In addition, the policy name must match the model name and have a `Policy` suffix. So, a `User` model would correspond to a `UserPolicy` policy class. -If you would like to provide your own policy discovery logic, you may register a custom callback using the `Gate::guessPolicyNamesUsing` method. Typically, this method should be called from the `boot` method of your application's `AuthServiceProvider`: +If you would like to define your own policy discovery logic, you may register a custom policy discovery callback using the `Gate::guessPolicyNamesUsing` method. Typically, this method should be called from the `boot` method of your application's `AuthServiceProvider`: use Illuminate\Support\Facades\Gate; Gate::guessPolicyNamesUsing(function ($modelClass) { - // return policy class name... + // Return the name of the policy class for the given model... }); > {note} Any policies that are explicitly mapped in your `AuthServiceProvider` will take precedence over any potentially auto-discovered policies. @@ -294,9 +294,9 @@ If you would like to provide your own policy discovery logic, you may register a <a name="policy-methods"></a> ### Policy Methods -Once the policy has been registered, you may add methods for each action it authorizes. For example, let's define an `update` method on our `PostPolicy` which determines if a given `User` can update a given `Post` instance. +Once the policy class has been registered, you may add methods for each action it authorizes. For example, let's define an `update` method on our `PostPolicy` which determines if a given `App\Models\User` can update a given `App\Models\Post` instance. -The `update` method will receive a `User` and a `Post` instance as its arguments, and should return `true` or `false` indicating whether the user is authorized to update the given `Post`. So, for this example, let's verify that the user's `id` matches the `user_id` on the post: +The `update` method will receive a `User` and a `Post` instance as its arguments, and should return `true` or `false` indicating whether the user is authorized to update the given `Post`. So, in this example, we will verify that the user's `id` matches the `user_id` on the post: <?php @@ -320,15 +320,19 @@ The `update` method will receive a `User` and a `Post` instance as its arguments } } -You may continue to define additional methods on the policy as needed for the various actions it authorizes. For example, you might define `view` or `delete` methods to authorize various `Post` actions, but remember you are free to give your policy methods any name you like. +You may continue to define additional methods on the policy as needed for the various actions it authorizes. For example, you might define `view` or `delete` methods to authorize various `Post` related actions, but remember you are free to give your policy methods any name you like. -> {tip} If you used the `--model` option when generating your policy via the Artisan console, it will already contain methods for the `viewAny`, `view`, `create`, `update`, `delete`, `restore`, and `forceDelete` actions. +If you used the `--model` option when generating your policy via the Artisan console, it will already contain methods for the `viewAny`, `view`, `create`, `update`, `delete`, `restore`, and `forceDelete` actions. + +> {tip} All policies are resolved via the Laravel [service container](/docs/{{version}}/container), allowing you to type-hint any needed dependencies in the policy's constructor to have them automatically injected. <a name="policy-responses"></a> ### Policy Responses -So far, we have only examined policy methods that return simple boolean values. However, sometimes you may wish to return a more detailed response, including an error message. To do so, you may return an `Illuminate\Auth\Access\Response` from your policy method: +So far, we have only examined policy methods that return simple boolean values. However, sometimes you may wish to return a more detailed response, including an error message. To do so, you may return an `Illuminate\Auth\Access\Response` instance from your policy method: + use App\Models\Post; + use App\Models\User; use Illuminate\Auth\Access\Response; /** @@ -347,6 +351,8 @@ So far, we have only examined policy methods that return simple boolean values. When returning an authorization response from your policy, the `Gate::allows` method will still return a simple boolean value; however, you may use the `Gate::inspect` method to get the full authorization response returned by the gate: + use Illuminate\Support\Facades\Gate; + $response = Gate::inspect('update', $post); if ($response->allowed()) { @@ -355,7 +361,7 @@ When returning an authorization response from your policy, the `Gate::allows` me echo $response->message(); } -Of course, when using the `Gate::authorize` method to throw an `AuthorizationException` if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response: +When using the `Gate::authorize` method, which throws an `AuthorizationException` if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response: Gate::authorize('update', $post); @@ -364,9 +370,7 @@ Of course, when using the `Gate::authorize` method to throw an `AuthorizationExc <a name="methods-without-models"></a> ### Methods Without Models -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, it will not receive a model instance. Instead, you should define the method as only expecting the authenticated user: +Some policy methods only receive an instance of the currently authenticated user. This situation is most common when authorizing `create` actions. For example, if you are creating a blog, you may wish to determine if a user is authorized to create any posts at all. In these situations, your policy method should only expect to receive a user instance: /** * Determine if the given user can create posts. @@ -376,7 +380,7 @@ When defining policy methods that will not receive a model instance, such as a ` */ public function create(User $user) { - // + return $user->role == 'writer'; } <a name="guest-users"></a> @@ -411,14 +415,23 @@ By default, all gates and policies automatically return `false` if the incoming For certain users, you may wish to authorize all actions within a given policy. To accomplish this, define a `before` method on the policy. The `before` method will be executed before any other methods on the policy, giving you an opportunity to authorize the action before the intended policy method is actually called. This feature is most commonly used for authorizing application administrators to perform any action: - public function before($user, $ability) + use App\Models\User; + + /** + * Perform pre-authorization checks. + * + * @param \App\Models\User $user + * @param string $ability + * @return void|bool + */ + public function before(User $user, $ability) { - if ($user->isSuperAdmin()) { + if ($user->isAdministrator()) { return true; } } -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. +If you would like to deny all authorization checks for a particular type of user then you may return `false` from the `before` method. If `null` is returned, the authorization check will fall through to the policy method. > {note} The `before` method of a policy class will not be called if the class doesn't contain a method with a name matching the name of the ability being checked. @@ -428,10 +441,33 @@ If you would like to deny all authorizations for a user you should return `false <a name="via-the-user-model"></a> ### Via The User Model -The `User` model that is included with your Laravel application includes two helpful methods for authorizing actions: `can` and `cant`. The `can` method receives the action you wish to authorize and the relevant model. For example, let's determine if a user is authorized to update a given `Post` model: +The `App\Models\User` model that is included with your Laravel application includes two helpful methods for authorizing actions: `can` and `cant`. The `can` method receives the name of the action you wish to authorize and the relevant model. For example, let's determine if a user is authorized to update a given `App\Models\Post` model. Typically, this will be done within a controller method: + + <?php + + namespace App\Http\Controllers; + + use App\Http\Controllers\Controller; + use App\Models\Post; + use Illuminate\Http\Request; + + class PostController extends Controller + { + /** + * Update the given post. + * + * @param \Illuminate\Http\Request $request + * @param \App\Models\Post $post + * @return \Illuminate\Http\Response + */ + public function update(Request $request, Post $post) + { + if (! $request->user()->can('update', $post)) { + abort(403); + } - if ($user->can('update', $post)) { - // + // Update the post... + } } If a [policy is registered](#registering-policies) for the given model, the `can` method will automatically call the appropriate policy and return the boolean result. If no policy is registered for the model, the `can` method will attempt to call the closure based Gate matching the given action name. @@ -439,18 +475,38 @@ If a [policy is registered](#registering-policies) for the given model, the `can <a name="user-model-actions-that-dont-require-models"></a> #### Actions That Don't Require Models -Remember, some actions like `create` may not require a model instance. In these situations, you may pass a class name to the `can` method. The class name will be used to determine which policy to use when authorizing the action: +Remember, some actions may correspond to policy methods like `create` that do not require a model instance. In these situations, you may pass a class name to the `can` method. The class name will be used to determine which policy to use when authorizing the action: + + <?php + + namespace App\Http\Controllers; + use App\Http\Controllers\Controller; use App\Models\Post; + use Illuminate\Http\Request; - if ($user->can('create', Post::class)) { - // Executes the "create" method on the relevant policy... + class PostController extends Controller + { + /** + * Create a post. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function store(Request $request) + { + if (! $request->user()->can('create', Post::class)) { + abort(403); + } + + // Create the post... + } } <a name="via-middleware"></a> ### Via Middleware -Laravel includes a middleware that can authorize actions before the incoming request even reaches your routes or controllers. By default, the `Illuminate\Auth\Middleware\Authorize` middleware is assigned the `can` key in your `App\Http\Kernel` class. Let's explore an example of using the `can` middleware to authorize that a user can update a blog post: +Laravel includes a middleware that can authorize actions before the incoming request even reaches your routes or controllers. By default, the `Illuminate\Auth\Middleware\Authorize` middleware is assigned the `can` key in your `App\Http\Kernel` class. Let's explore an example of using the `can` middleware to authorize that a user can update a post: use App\Models\Post; @@ -458,12 +514,12 @@ Laravel includes a middleware that can authorize actions before the incoming req // The current user may update the post... })->middleware('can:update,post'); -In this example, we're passing the `can` middleware two arguments. The first is the name of the action we wish to authorize and the second is the route parameter we wish to pass to the policy method. In this case, since we are using [implicit model binding](/docs/{{version}}/routing#implicit-binding), a `Post` model will be passed to the policy method. If the user is not authorized to perform the given action, an HTTP response with a `403` status code will be generated by the middleware. +In this example, we're passing the `can` middleware two arguments. The first is the name of the action we wish to authorize and the second is the route parameter we wish to pass to the policy method. In this case, since we are using [implicit model binding](/docs/{{version}}/routing#implicit-binding), a `App\Models\Post` model will be passed to the policy method. If the user is not authorized to perform the given action, an HTTP response with a 403 status code will be returned by the middleware. <a name="middleware-actions-that-dont-require-models"></a> #### Actions That Don't Require Models -Again, some actions like `create` may not require a model instance. In these situations, you may pass a class name to the middleware. The class name will be used to determine which policy to use when authorizing the action: +Again, some policy methods like `create` do not require a model instance. In these situations, you may pass a class name to the middleware. The class name will be used to determine which policy to use when authorizing the action: Route::post('/post', function () { // The current user may create posts... @@ -472,7 +528,9 @@ Again, some actions like `create` may not require a model instance. In these sit <a name="via-controller-helpers"></a> ### Via Controller Helpers -In addition to helpful methods provided to the `User` model, Laravel provides a helpful `authorize` method to any of your controllers which extend the `App\Http\Controllers\Controller` base class. Like the `can` method, this method accepts the name of the action you wish to authorize and the relevant model. If the action is not authorized, the `authorize` method will throw an `Illuminate\Auth\Access\AuthorizationException`, which the default Laravel exception handler will convert to an HTTP response with a `403` status code: +In addition to helpful methods provided to the `App\Models\User` model, Laravel provides a helpful `authorize` method to any of your controllers which extend the `App\Http\Controllers\Controller` base class. + +Like the `can` method, this method accepts the name of the action you wish to authorize and the relevant model. If the action is not authorized, the `authorize` method will throw an `Illuminate\Auth\Access\AuthorizationException` exception which the Laravel exception handler will automatically convert to an HTTP response with a 403 status code: <?php @@ -487,9 +545,10 @@ In addition to helpful methods provided to the `User` model, Laravel provides a /** * Update the given blog post. * - * @param Request $request - * @param Post $post - * @return Response + * @param \Illuminate\Http\Request $request + * @param \App\Models\Post $post + * @return \Illuminate\Http\Response + * * @throws \Illuminate\Auth\Access\AuthorizationException */ public function update(Request $request, Post $post) @@ -503,13 +562,17 @@ In addition to helpful methods provided to the `User` model, Laravel provides a <a name="controller-actions-that-dont-require-models"></a> #### Actions That Don't Require Models -As previously discussed, some actions like `create` may not require a model instance. In these situations, you should pass a class name to the `authorize` method. The class name will be used to determine which policy to use when authorizing the action: +As previously discussed, some policy methods like `create` do not require a model instance. In these situations, you should pass a class name to the `authorize` method. The class name will be used to determine which policy to use when authorizing the action: + + use App\Models\Post; + use Illuminate\Http\Request; /** * Create a new blog post. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + * * @throws \Illuminate\Auth\Access\AuthorizationException */ public function create(Request $request) @@ -522,9 +585,9 @@ As previously discussed, some actions like `create` may not require a model inst <a name="authorizing-resource-controllers"></a> #### Authorizing Resource Controllers -If you are utilizing [resource controllers](/docs/{{version}}/controllers#resource-controllers), you may make use of the `authorizeResource` method in the controller's constructor. This method will attach the appropriate `can` middleware definitions to the resource controller's methods. +If you are utilizing [resource controllers](/docs/{{version}}/controllers#resource-controllers), you may make use of the `authorizeResource` method in your controller's constructor. This method will attach the appropriate `can` middleware definitions to the resource controller's methods. -The `authorizeResource` method accepts the model's class name as its first argument, and the name of the route / request parameter that will contain the model's ID as its second argument. You should ensure your [resource controller](/docs/{{version}}/controllers#resource-controllers) is created with the `--model` flag to have the required method signatures and type hints: +The `authorizeResource` method accepts the model's class name as its first argument, and the name of the route / request parameter that will contain the model's ID as its second argument. You should ensure your [resource controller](/docs/{{version}}/controllers#resource-controllers) is created using the `--model` flag so that it has the required method signatures and type hints: <?php @@ -536,13 +599,18 @@ The `authorizeResource` method accepts the model's class name as its first argum class PostController extends Controller { + /** + * Create the controller instance. + * + * @return void + */ public function __construct() { $this->authorizeResource(Post::class, 'post'); } } -The following controller methods will be mapped to their corresponding policy method: +The following controller methods will be mapped to their corresponding policy method. When requests are routed to the given controller method, the corresponding policy method will automatically be invoked before the controller method is executed: | Controller Method | Policy Method | | --- | --- | @@ -559,50 +627,58 @@ The following controller methods will be mapped to their corresponding policy me <a name="via-blade-templates"></a> ### 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` family of 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` directives: - @can('update', $post) - <!-- The Current User Can Update The Post --> - @elsecan('create', App\Models\Post::class) - <!-- The Current User Can Create New Post --> - @endcan +```html +@can('update', $post) + <!-- The current user can update the post... --> +@elsecan('create', App\Models\Post::class) + <!-- The current user can create new posts... --> +@endcan - @cannot('update', $post) - <!-- The Current User Cannot Update The Post --> - @elsecannot('create', App\Models\Post::class) - <!-- The Current User Cannot Create A New Post --> - @endcannot +@cannot('update', $post) + <!-- The current user cannot update the post... --> +@elsecannot('create', App\Models\Post::class) + <!-- The current user can now create new posts... --> +@endcannot +``` -These directives are convenient shortcuts 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 are equivalent to the following statements: - @if (Auth::user()->can('update', $post)) - <!-- The Current User Can Update The Post --> - @endif +```html +@if (Auth::user()->can('update', $post)) + <!-- The current user can update the post... --> +@endif - @unless (Auth::user()->can('update', $post)) - <!-- The Current User Cannot Update The Post --> - @endunless +@unless (Auth::user()->can('update', $post)) + <!-- The current user cannot update the post... --> +@endunless +``` -You may also determine if a user has any authorization ability from a given list of abilities. To accomplish this, use the `@canany` directive: +You may also determine if a user is authorized to perform any action from a given array of actions. To accomplish this, use the `@canany` directive: - @canany(['update', 'view', 'delete'], $post) - // The current user can update, view, or delete the post - @elsecanany(['create'], \App\Models\Post::class) - // The current user can create a post - @endcanany +```html +@canany(['update', 'view', 'delete'], $post) + <!-- The current user can update, view, or delete the post... --> +@elsecanany(['create'], \App\Models\Post::class) + <!-- The current user can create a post... --> +@endcanany +``` <a name="blade-actions-that-dont-require-models"></a> #### Actions That Don't Require Models Like most of the other authorization methods, you may pass a class name to the `@can` and `@cannot` directives if the action does not require a model instance: - @can('create', App\Models\Post::class) - <!-- The Current User Can Create Posts --> - @endcan +```html +@can('create', App\Models\Post::class) + <!-- The current user can create posts... --> +@endcan - @cannot('create', App\Models\Post::class) - <!-- The Current User Can't Create Posts --> - @endcannot +@cannot('create', App\Models\Post::class) + <!-- The current user can't create posts... --> +@endcannot +``` <a name="supplying-additional-context"></a> ### Supplying Additional Context @@ -620,7 +696,7 @@ When authorizing actions using policies, you may pass an array as the second arg public function update(User $user, Post $post, int $category) { return $user->id === $post->user_id && - $category > 3; + $user->canUpdateCategory($category); } When attempting to determine if the authenticated user can update a given post, we can invoke this policy method like so: @@ -628,14 +704,15 @@ When attempting to determine if the authenticated user can update a given post, /** * Update the given blog post. * - * @param Request $request - * @param Post $post - * @return Response + * @param \Illuminate\Http\Request $request + * @param \App\Models\Post $post + * @return \Illuminate\Http\Response + * * @throws \Illuminate\Auth\Access\AuthorizationException */ public function update(Request $request, Post $post) { - $this->authorize('update', [$post, $request->input('category')]); + $this->authorize('update', [$post, $request->category]); // The current user can update the blog post... } From a39cbda82a77ec905170e910cd52f61f273e6358 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 15:16:45 -0600 Subject: [PATCH 139/274] wip --- authorization.md | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/authorization.md b/authorization.md index a372aa84e2..baaad5ecf4 100644 --- a/authorization.md +++ b/authorization.md @@ -17,8 +17,8 @@ - [Policy Filters](#policy-filters) - [Authorizing Actions Using Policies](#authorizing-actions-using-policies) - [Via The User Model](#via-the-user-model) - - [Via Middleware](#via-middleware) - [Via Controller Helpers](#via-controller-helpers) + - [Via Middleware](#via-middleware) - [Via Blade Templates](#via-blade-templates) - [Supplying Additional Context](#supplying-additional-context) @@ -37,6 +37,8 @@ You do not need to choose between exclusively using gates or exclusively using p <a name="writing-gates"></a> ### Writing Gates +> {note} Gates are a great way to learn the basics of Laravel's authorization features; however, when building robust Laravel applications you should consider using [policies](#creating-policies) to organize your authorization rules. + Gates are closures that determine if a user is authorized to perform a given action. Typically, gates are defined within the `boot` method of 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. In this example, we'll define a gate to determine if a user can update a given `App\Models\Post` model. The gate will accomplish this may comparing the user's `id` against the `user_id` of the user that created the post: @@ -503,28 +505,6 @@ Remember, some actions may correspond to policy methods like `create` that do no } } -<a name="via-middleware"></a> -### Via Middleware - -Laravel includes a middleware that can authorize actions before the incoming request even reaches your routes or controllers. By default, the `Illuminate\Auth\Middleware\Authorize` middleware is assigned the `can` key in your `App\Http\Kernel` class. Let's explore an example of using the `can` middleware to authorize that a user can update a post: - - use App\Models\Post; - - Route::put('/post/{post}', function (Post $post) { - // The current user may update the post... - })->middleware('can:update,post'); - -In this example, we're passing the `can` middleware two arguments. The first is the name of the action we wish to authorize and the second is the route parameter we wish to pass to the policy method. In this case, since we are using [implicit model binding](/docs/{{version}}/routing#implicit-binding), a `App\Models\Post` model will be passed to the policy method. If the user is not authorized to perform the given action, an HTTP response with a 403 status code will be returned by the middleware. - -<a name="middleware-actions-that-dont-require-models"></a> -#### Actions That Don't Require Models - -Again, some policy methods like `create` do not require a model instance. In these situations, you may pass a class name to the middleware. The class name will be used to determine which policy to use when authorizing the action: - - Route::post('/post', function () { - // The current user may create posts... - })->middleware('can:create,App\Models\Post'); - <a name="via-controller-helpers"></a> ### Via Controller Helpers @@ -624,6 +604,28 @@ The following controller methods will be mapped to their corresponding policy me > {tip} You may use the `make:policy` command with the `--model` option to quickly generate a policy class for a given model: `php artisan make:policy PostPolicy --model=Post`. +<a name="via-middleware"></a> +### Via Middleware + +Laravel includes a middleware that can authorize actions before the incoming request even reaches your routes or controllers. By default, the `Illuminate\Auth\Middleware\Authorize` middleware is assigned the `can` key in your `App\Http\Kernel` class. Let's explore an example of using the `can` middleware to authorize that a user can update a post: + + use App\Models\Post; + + Route::put('/post/{post}', function (Post $post) { + // The current user may update the post... + })->middleware('can:update,post'); + +In this example, we're passing the `can` middleware two arguments. The first is the name of the action we wish to authorize and the second is the route parameter we wish to pass to the policy method. In this case, since we are using [implicit model binding](/docs/{{version}}/routing#implicit-binding), a `App\Models\Post` model will be passed to the policy method. If the user is not authorized to perform the given action, an HTTP response with a 403 status code will be returned by the middleware. + +<a name="middleware-actions-that-dont-require-models"></a> +#### Actions That Don't Require Models + +Again, some policy methods like `create` do not require a model instance. In these situations, you may pass a class name to the middleware. The class name will be used to determine which policy to use when authorizing the action: + + Route::post('/post', function () { + // The current user may create posts... + })->middleware('can:create,App\Models\Post'); + <a name="via-blade-templates"></a> ### Via Blade Templates From 808efbffc37b6e899309aadf98d97f67d2335c54 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 15:28:03 -0600 Subject: [PATCH 140/274] wip --- authorization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authorization.md b/authorization.md index baaad5ecf4..85e0b82cd6 100644 --- a/authorization.md +++ b/authorization.md @@ -25,7 +25,7 @@ <a name="introduction"></a> ## Introduction -In addition to providing built-in [authentication](/docs/{{version}}/authentication) services, Laravel also provides a simple way to authorize user actions against a given resource. For example, even though a user is authenticated, they may not be authorized to update or delete certain Eloquent models or database records managed by your application. +In addition to providing built-in [authentication](/docs/{{version}}/authentication) services, Laravel also provides a simple way to authorize user actions against a given resource. For example, even though a user is authenticated, they may not be authorized to update or delete certain Eloquent models or database records managed by your application. Laravel's authorization features provide an easy, organized way of managing these types of authorization checks. Laravel provides two primary ways of authorizing actions: gates and policies. Think of gates and policies like routes and controllers. Gates provide a simple, closure based approach to authorization while policies, like controllers, group logic around a particular model or resource. In this documentation, we'll explore gates first and then examine policies. From 20e0da75e8285a1fb70ab07964decd36f35e4bca Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 15:32:43 -0600 Subject: [PATCH 141/274] wip --- authorization.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/authorization.md b/authorization.md index 85e0b82cd6..0da25bb256 100644 --- a/authorization.md +++ b/authorization.md @@ -27,9 +27,9 @@ In addition to providing built-in [authentication](/docs/{{version}}/authentication) services, Laravel also provides a simple way to authorize user actions against a given resource. For example, even though a user is authenticated, they may not be authorized to update or delete certain Eloquent models or database records managed by your application. Laravel's authorization features provide an easy, organized way of managing these types of authorization checks. -Laravel provides two primary ways of authorizing actions: gates and policies. Think of gates and policies like routes and controllers. Gates provide a simple, closure based approach to authorization while policies, like controllers, group logic around a particular model or resource. In this documentation, we'll explore gates first and then examine policies. +Laravel provides two primary ways of authorizing actions: [gates](#gates) and [policies](#creating-policies). Think of gates and policies like routes and controllers. Gates provide a simple, closure based approach to authorization while policies, like controllers, group logic around a particular model or resource. In this documentation, we'll explore gates first and then examine policies. -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. +You do not need to choose between exclusively using gates or exclusively using policies when building an application. Most applications will most likely contain some 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. <a name="gates"></a> ## Gates @@ -39,7 +39,7 @@ You do not need to choose between exclusively using gates or exclusively using p > {note} Gates are a great way to learn the basics of Laravel's authorization features; however, when building robust Laravel applications you should consider using [policies](#creating-policies) to organize your authorization rules. -Gates are closures that determine if a user is authorized to perform a given action. Typically, gates are defined within the `boot` method of 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. +Gates are simply closures that determine if a user is authorized to perform a given action. Typically, gates are defined within the `boot` method of 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. In this example, we'll define a gate to determine if a user can update a given `App\Models\Post` model. The gate will accomplish this may comparing the user's `id` against the `user_id` of the user that created the post: From 63325691a94c9b0f64efae9516ae0ec6e2b277f9 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 15:45:45 -0600 Subject: [PATCH 142/274] wip --- verification.md | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/verification.md b/verification.md index 93c611ae2e..a44adb15d6 100644 --- a/verification.md +++ b/verification.md @@ -13,14 +13,14 @@ <a name="introduction"></a> ## Introduction -Many web applications require users to verify their email addresses before using the application. Rather than forcing you to re-implement this on each application, Laravel provides convenient methods for sending and verifying email verification requests. +Many web applications require users to verify their email addresses before using the application. Rather than forcing you to re-implement this feature by hand for each application you create, Laravel provides convenient built-in services for sending and verifying email verification requests. -> {tip} Want to get started fast? Install one of the [Laravel application starter kits](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system, including email verification support! +> {tip} Want to get started fast? Install one of the [Laravel application starter kits](/docs/{{version}}/starter-kits) in a fresh Laravel application. The starter kits will take care of scaffolding your entire authentication system, including email verification support. <a name="model-preparation"></a> ### Model Preparation -To get started, verify that your `App\Models\User` model implements the `Illuminate\Contracts\Auth\MustVerifyEmail` contract: +Before getting started, verify that your `App\Models\User` model implements the `Illuminate\Contracts\Auth\MustVerifyEmail` contract: <?php @@ -37,7 +37,7 @@ To get started, verify that your `App\Models\User` model implements the `Illumin // ... } -Once this interface has been added to your model, newly registered users will automatically be sent an email containing an email verification link. As you can see by examining your `EventServiceProvider`, Laravel already contains a `SendEmailVerificationNotification` [listener](/docs/{{version}}/events) that is attached to the `Illuminate\Auth\Events\Registered` event. +Once this interface has been added to your model, newly registered users will automatically be sent an email containing an email verification link. As you can see by examining your application's `App\Providers\EventServiceProvider`, Laravel already contains a `SendEmailVerificationNotification` [listener](/docs/{{version}}/events) that is attached to the `Illuminate\Auth\Events\Registered` event. This event listener will send the email verification link to the user. If you are manually implementing registration within your application instead of using [a starter kit](/docs/{{version}}/starter-kits), you should ensure that you are dispatching the `Illuminate\Auth\Events\Registered` event after a user's registration is successful: @@ -48,23 +48,27 @@ If you are manually implementing registration within your application instead of <a name="database-preparation"></a> ### Database Preparation -Next, your `user` table must contain an `email_verified_at` column to store the date and time that the email address was verified. By default, the `users` table migration included with the Laravel framework already includes this column. So, all you need to do is run your database migrations: +Next, your `user` table must contain an `email_verified_at` column to store the date and time that the user's email address was verified. By default, the `users` table migration included with the Laravel framework already includes this column. So, all you need to do is run your database migrations: php artisan migrate <a name="verification-routing"></a> ## Routing -To properly implement email verification, three routes will need to be defined. First, a route will be needed to display a notice to the user that they should click the email verification link in the verification email that Laravel sent them after registration. Second, a route will be needed to handle requests generated when the user clicks the email verification link in the email. Third, a route will be needed to resend a verification link if the user accidentally loses the first one. +To properly implement email verification, three routes will need to be defined. First, a route will be needed to display a notice to the user that they should click the email verification link in the verification email that Laravel sent them after registration. + +Second, a route will be needed to handle requests generated when the user clicks the email verification link in the email. + +Third, a route will be needed to resend a verification link if the user accidentally loses the first verification link. <a name="the-email-verification-notice"></a> ### The Email Verification Notice -As mentioned previously, a route should be defined that will return a view instructing the user to click the email verification link that was emailed to them by Laravel. This view will be displayed to users when they try to access other parts of the application without verifying their email address first. Remember, the link is automatically emailed to the user as long as your `App\Models\User` model implements the `MustVerifyEmail` interface: +As mentioned previously, a route should be defined that will return a view instructing the user to click the email verification link that was emailed to them by Laravel after registration. This view will be displayed to users when they try to access other parts of the application without verifying their email address first. Remember, the link is automatically emailed to the user as long as your `App\Models\User` model implements the `MustVerifyEmail` interface: Route::get('/email/verify', function () { return view('auth.verify-email'); - })->middleware(['auth'])->name('verification.notice'); + })->middleware('auth')->name('verification.notice'); The route that returns the email verification notice should be named `verification.notice`. It is important that the route be assigned this exact name since the `verified` middleware [included with Laravel](#protecting-routes) will automatically redirect to this route name if a user has not verified their email address. @@ -73,7 +77,7 @@ The route that returns the email verification notice should be named `verificati <a name="the-email-verification-handler"></a> ### The Email Verification Handler -Next, we need a route that will handle requests generated when the user clicks the email verification link that was emailed to them. This route should be named `verification.verify` and be assigned the `auth` and `signed` middlewares: +Next, we need to define a route that will handle requests generated when the user clicks the email verification link that was emailed to them. This route should be named `verification.verify` and be assigned the `auth` and `signed` middlewares: use Illuminate\Foundation\Auth\EmailVerificationRequest; use Illuminate\Http\Request; @@ -84,7 +88,7 @@ Next, we need a route that will handle requests generated when the user clicks t return redirect('/home'); })->middleware(['auth', 'signed'])->name('verification.verify'); -Before moving on, let's take a closer look at this route. First, you'll notice we are using an `EmailVerificationRequest` request type instead of the typical `Illuminate\Http\Request` instance. The `EmailVerificationRequest` is a [form request](/docs/{{version}}/validation#form-request-validation) that is included with Laravel. This request will take care of automatically validating the request's `id` and `hash` parameters. +Before moving on, let's take a closer look at this route. First, you'll notice we are using an `EmailVerificationRequest` request type instead of the typical `Illuminate\Http\Request` instance. The `EmailVerificationRequest` is a [form request](/docs/{{version}}/validation#form-request-validation) that is included with Laravel. This request will automatically take care of validating the request's `id` and `hash` parameters. Next, we can proceed directly to calling the `fulfill` method on the request. This method will call the `markEmailAsVerified` method on the authenticated user and dispatch the `Illuminate\Auth\Events\Verified` event. The `markEmailAsVerified` method is available to the default `App\Models\User` model via the `Illuminate\Foundation\Auth\User` base class. Once the user's email address has been verified, you may redirect them wherever you wish. @@ -98,16 +102,16 @@ Sometimes a user may misplace or accidentally delete the email address verificat Route::post('/email/verification-notification', function (Request $request) { $request->user()->sendEmailVerificationNotification(); - return back()->with('status', 'verification-link-sent'); + return back()->with('message', 'Verification link sent!'); })->middleware(['auth', 'throttle:6,1'])->name('verification.send'); <a name="protecting-routes"></a> ### Protecting Routes -[Route middleware](/docs/{{version}}/middleware) can be used to only allow verified users to access a given route. Laravel ships with a `verified` middleware, which references the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` class. Since this middleware is already registered in your application's HTTP kernel, all you need to do is attach the middleware to a route definition: +[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel ships with a `verified` middleware, which references the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` class. Since this middleware is already registered in your application's HTTP kernel, all you need to do is attach the middleware to a route definition: - Route::get('profile', function () { - // Only verified users may enter... + Route::get('/profile', function () { + // Only verified users may access this route... })->middleware('verified'); If an unverified user attempts to access a route that has been assigned this middleware, they will automatically be redirected to the `verification.notice` [named route](/docs/{{version}}/routing#named-routes). @@ -115,7 +119,7 @@ If an unverified user attempts to access a route that has been assigned this mid <a name="events"></a> ## Events -When using the [Laravel application starter kits](/docs/{{version}}/starter-kits), Laravel dispatches [events](/docs/{{version}}/events) during the email verification process. If you are manually handling email verification for your application, you may wish to manually dispatch these events after verification is completed. You may attach listeners to these events in your `EventServiceProvider`: +When using the [Laravel application starter kits](/docs/{{version}}/starter-kits), Laravel dispatches [events](/docs/{{version}}/events) during the email verification process. If you are manually handling email verification for your application, you may wish to manually dispatch these events after verification is completed. You may attach listeners to these events in your application's `EventServiceProvider`: /** * The event listener mappings for the application. From 344139183dd6acc9e5f056d083cc912d22e50dea Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 15:59:56 -0600 Subject: [PATCH 143/274] wip --- encryption.md | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/encryption.md b/encryption.md index bcc5223fd8..42cc97df4d 100644 --- a/encryption.md +++ b/encryption.md @@ -7,12 +7,12 @@ <a name="introduction"></a> ## Introduction -Laravel's encrypter uses OpenSSL to provide AES-256 and AES-128 encryption. You are strongly encouraged to use Laravel's built-in encryption facilities and not attempt to roll your own "home grown" encryption algorithms. All of Laravel's encrypted values are signed using a message authentication code (MAC) so that their underlying value can not be modified once encrypted. +Laravel's encryption services provide a simple, convenient interface for encrypting and decrypting text via OpenSSL using AES-256 and AES-128 encryption. All of Laravel's encrypted values are signed using a message authentication code (MAC) so that their underlying value can not be modified or tampered with once encrypted. <a name="configuration"></a> ## Configuration -Before using Laravel's encrypter, you must set a `key` option in your `config/app.php` configuration file. You should use the `php artisan key:generate` command to generate this key since this Artisan command will use PHP's secure random bytes generator to build your key. If this value is not properly set, all values encrypted by Laravel will be insecure. +Before using Laravel's encrypter, you must set the `key` configuration option in your `config/app.php` configuration file. This configuration value is driven by the `APP_KEY` environment variable. You should use the `php artisan key:generate` command to generate this variable's value since the `key:generate` command will use PHP's secure random bytes generator to build a cryptographically secure key for your application. Typically, the value of the `APP_KEY` environment variable will be generated for you during [Laravel's installation](/docs/{{version}}/installation). <a name="using-the-encrypter"></a> ## Using The Encrypter @@ -20,7 +20,7 @@ Before using Laravel's encrypter, you must set a `key` option in your `config/ap <a name="encrypting-a-value"></a> #### Encrypting A Value -You may encrypt a value using the `encryptString` method of the `Crypt` facade. All encrypted values are encrypted using OpenSSL and the `AES-256-CBC` cipher. Furthermore, all encrypted values are signed with a message authentication code (MAC) to detect any modifications to the encrypted string: +You may encrypt a value using the `encryptString` method provided by the `Crypt` facade. All encrypted values are encrypted using OpenSSL and the AES-256-CBC cipher. Furthermore, all encrypted values are signed with a message authentication code (MAC). The integrated message authentication code will prevent the decryption of any values that have been tampered with by malicious users: <?php @@ -31,21 +31,18 @@ You may encrypt a value using the `encryptString` method of the `Crypt` facade. use Illuminate\Http\Request; use Illuminate\Support\Facades\Crypt; - class UserController extends Controller + class DigitalOceanTokenController extends Controller { /** - * Store a secret message for the user. + * Store a DigitalOcean API token for the user. * - * @param Request $request - * @param int $id - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ - public function storeSecret(Request $request, $id) + public function storeSecret(Request $request) { - $user = User::findOrFail($id); - - $user->fill([ - 'secret' => Crypt::encryptString($request->secret), + $request->user()->fill([ + 'token' => Crypt::encryptString($request->token), ])->save(); } } @@ -53,7 +50,7 @@ You may encrypt a value using the `encryptString` method of the `Crypt` facade. <a name="decrypting-a-value"></a> #### Decrypting A Value -You may decrypt values using the `decryptString` method of the `Crypt` facade. If the value can not be properly decrypted, such as when the MAC is invalid, an `Illuminate\Contracts\Encryption\DecryptException` will be thrown: +You may decrypt values using the `decryptString` method provided by the `Crypt` facade. If the value can not be properly decrypted, such as when the message authentication code is invalid, an `Illuminate\Contracts\Encryption\DecryptException` will be thrown: use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Support\Facades\Crypt; From 706bb976db8ab4d388177dc18d2491bfd01c95cd Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 16:08:02 -0600 Subject: [PATCH 144/274] wip --- hashing.md | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/hashing.md b/hashing.md index 9a7fc4cb14..6988b9048f 100644 --- a/hashing.md +++ b/hashing.md @@ -3,24 +3,30 @@ - [Introduction](#introduction) - [Configuration](#configuration) - [Basic Usage](#basic-usage) + - [Hashing Passwords](#hashing-passwords) + - [Verifying That A Password Matches A Hash](#verifying-that-a-password-matches-a-hash) + - [Determining If A Password Needs To Be Rehashed](#determining-if-a-password-needs-to-be-rehashed) <a name="introduction"></a> ## Introduction -The Laravel `Hash` [facade](/docs/{{version}}/facades) provides secure Bcrypt and Argon2 hashing for storing user passwords. If you are using the [Laravel application starter kits](/docs/{{version}}/starter-kits), Bcrypt will be used for registration and authentication by default. +The Laravel `Hash` [facade](/docs/{{version}}/facades) provides secure Bcrypt and Argon2 hashing for storing user passwords. If you are using one of the [Laravel application starter kits](/docs/{{version}}/starter-kits), Bcrypt will be used for registration and authentication by default. -> {tip} Bcrypt is a great choice for hashing passwords because its "work factor" is adjustable, which means that the time it takes to generate a hash can be increased as hardware power increases. +Bcrypt is a great choice for hashing passwords because its "work factor" is adjustable, which means that the time it takes to generate a hash can be increased as hardware power increases. When hashing passwords, slow is good. The longer an algorithm takes to hash a password, the longer it takes malicious users to generate "rainbow tables" of all possible string hash values that may be used in brute force attacks against applications. <a name="configuration"></a> ## Configuration -The default hashing driver for your application is configured in the `config/hashing.php` configuration file. There are currently three supported drivers: [Bcrypt](https://en.wikipedia.org/wiki/Bcrypt) and [Argon2](https://en.wikipedia.org/wiki/Argon2) (Argon2i and Argon2id variants). +The default hashing driver for your application is configured in your application's `config/hashing.php` configuration file. There are currently several supported drivers: [Bcrypt](https://en.wikipedia.org/wiki/Bcrypt) and [Argon2](https://en.wikipedia.org/wiki/Argon2) (Argon2i and Argon2id variants). > {note} The Argon2i driver requires PHP 7.2.0 or greater and the Argon2id driver requires PHP 7.3.0 or greater. <a name="basic-usage"></a> ## Basic Usage +<a name="hashing-passwords"></a> +### Hashing Passwords + You may hash a password by calling the `make` method on the `Hash` facade: <?php @@ -31,13 +37,13 @@ You may hash a password by calling the `make` method on the `Hash` facade: use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; - class UpdatePasswordController extends Controller + class PasswordController extends Controller { /** * Update the password for the user. * - * @param Request $request - * @return Response + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response */ public function update(Request $request) { @@ -52,7 +58,7 @@ You may hash a password by calling the `make` method on the `Hash` facade: <a name="adjusting-the-bcrypt-work-factor"></a> #### Adjusting The Bcrypt Work Factor -If you are using the Bcrypt algorithm, the `make` method allows you to manage the work factor of the algorithm using the `rounds` option; however, the default is acceptable for most applications: +If you are using the Bcrypt algorithm, the `make` method allows you to manage the work factor of the algorithm using the `rounds` option; however, the default work factor managed by Laravel is acceptable for most applications: $hashed = Hash::make('password', [ 'rounds' => 12, @@ -61,7 +67,7 @@ If you are using the Bcrypt algorithm, the `make` method allows you to manage th <a name="adjusting-the-argon2-work-factor"></a> #### Adjusting The Argon2 Work Factor -If you are using the Argon2 algorithm, the `make` method allows you to manage the work factor of the algorithm using the `memory`, `time`, and `threads` options; however, the defaults are acceptable for most applications: +If you are using the Argon2 algorithm, the `make` method allows you to manage the work factor of the algorithm using the `memory`, `time`, and `threads` options; however, the default values managed by Laravel are acceptable for most applications: $hashed = Hash::make('password', [ 'memory' => 1024, @@ -69,21 +75,21 @@ If you are using the Argon2 algorithm, the `make` method allows you to manage th 'threads' => 2, ]); -> {tip} For more information on these options, check out the [official PHP documentation](https://secure.php.net/manual/en/function.password-hash.php). +> {tip} For more information on these options, please refer to the [official PHP documentation regarding Argon hashing](https://secure.php.net/manual/en/function.password-hash.php). -<a name="verifying-a-password-against-a-hash"></a> -#### Verifying A Password Against A Hash +<a name="verifying-that-a-password-matches-a-hash"></a> +### Verifying That A Password Matches A Hash -The `check` method allows you to verify that a given plain-text string corresponds to a given hash: +The `check` method provided by the `Hash` facade allows you to verify that a given plain-text string corresponds to a given hash: if (Hash::check('plain-text', $hashedPassword)) { // The passwords match... } -<a name="checking-if-a-password-needs-to-be-rehashed"></a> -#### Checking If A Password Needs To Be Rehashed +<a name="determining-if-a-password-needs-to-be-rehashed"></a> +### Determining If A Password Needs To Be Rehashed -The `needsRehash` function allows you to determine if the work factor used by the hasher has changed since the password was hashed: +The `needsRehash` method provided by the `Hash` facade allows you to determine if the work factor used by the hasher has changed since the password was hashed. Some applications choose to perform this check during the application's authentication process: if (Hash::needsRehash($hashed)) { $hashed = Hash::make('plain-text'); From 7fc3980110096ca867af90ddba212c63487e3fa1 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 16:30:33 -0600 Subject: [PATCH 145/274] wip --- passwords.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/passwords.md b/passwords.md index e1c55567c4..d92546460a 100644 --- a/passwords.md +++ b/passwords.md @@ -11,28 +11,28 @@ <a name="introduction"></a> ## Introduction -Most web applications provide a way for users to reset their forgotten passwords. Rather than forcing you to re-implement this on each application, Laravel provides convenient methods for sending password reminders and performing password resets. +Most web applications provide a way for users to reset their forgotten passwords. Rather than forcing you to re-implement this by hand for every application you create, Laravel provides convenient services for sending password reset links and secure resetting passwords. -> {tip} Want to get started fast? Install an authentication [starter kit](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system, including resetting passwords! +> {tip} Want to get started fast? Install a Laravel [application starter kit](/docs/{{version}}/starter-kits) in a fresh Laravel application. Laravel's starter kits will take care of scaffolding your entire authentication system, including resetting forgotten passwords. <a name="model-preparation"></a> ### Model Preparation -Before using the password reset features of Laravel, your `App\Models\User` model must use the `Illuminate\Notifications\Notifiable` trait. Typically, this trait is automatically included on the default `App\Models\User` model that is included with Laravel. +Before using the password reset features of Laravel, your application's `App\Models\User` model must use the `Illuminate\Notifications\Notifiable` trait. Typically, this trait is already included on the default `App\Models\User` model that is created with new Laravel applications. Next, verify that your `App\Models\User` model implements the `Illuminate\Contracts\Auth\CanResetPassword` contract. The `App\Models\User` model included with the framework already implements this interface, and uses the `Illuminate\Auth\Passwords\CanResetPassword` trait to include the methods needed to implement the interface. <a name="database-preparation"></a> ### Database Preparation -A table must be created to store your application's password reset tokens. The migration for this table is included in the default Laravel installation, so you only need to migrate your database to create this table: +A table must be created to store your application's password reset tokens. The migration for this table is included in the default Laravel application, so you only need to migrate your database to create this table: php artisan migrate <a name="routing"></a> ## Routing -To properly implement support for allowing users to reset their passwords, we will need to define several routes. First, we will need a pair of routes to handle allowing the user to request a password reset link via their email address. Second, we will need a pair of routes to handle actually resetting the password once the user visits the password reset link that is emailed to them. +To properly implement support for allowing users to reset their passwords, we will need to define several routes. First, we will need a pair of routes to handle allowing the user to request a password reset link via their email address. Second, we will need a pair of routes to handle actually resetting the password once the user visits the password reset link that is emailed to them and completes the password reset form. <a name="requesting-the-password-reset-link"></a> ### Requesting The Password Reset Link @@ -44,14 +44,14 @@ First, we will define the routes that are needed to request password reset links Route::get('/forgot-password', function () { return view('auth.forgot-password'); - })->middleware(['guest'])->name('password.request'); + })->middleware('guest')->name('password.request'); The view that is returned by this route should have a form containing an `email` field, which will allow the user to request a password reset link for a given email address. <a name="password-reset-link-handling-the-form-submission"></a> #### Handling The Form Submission -Next, we will define a route will handle the form request from the "forgot password" view. This route will be responsible for validating the email address and sending the password reset request to the corresponding user: +Next, we will define a route that handles the form submission request from the "forgot password" view. This route will be responsible for validating the email address and sending the password reset request to the corresponding user: use Illuminate\Http\Request; use Illuminate\Support\Facades\Password; @@ -66,12 +66,14 @@ Next, we will define a route will handle the form request from the "forgot passw return $status === Password::RESET_LINK_SENT ? back()->with(['status' => __($status)]) : back()->withErrors(['email' => __($status)]); - })->middleware(['guest'])->name('password.email'); + })->middleware('guest')->name('password.email'); Before moving on, let's examine this route in more detail. First, the request's `email` attribute is validated. Next, we will use Laravel's built-in "password broker" (via the `Password` facade) to send a password reset link to the user. The password broker will take care of retrieving the user by the given field (in this case, the email address) and sending the user a password reset link via Laravel's built-in [notification system](/docs/{{version}}/notifications). The `sendResetLink` method returns a "status" slug. This status may be translated using Laravel's [localization](/docs/{{version}}/localization) helpers in order to display a user-friendly message to the user regarding the status of their request. The translation of the password reset status is determined by your application's `resources/lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. +You may wondering how Laravel knows how to retrieve the user record from your application's database when calling the `Password` facade's `sendResetLink` method. The Laravel password broker utilizes your authentication system's "user providers" to retrieve database records. The user provider used by the password broker is configured within the `passwords` configuration array of your `config/auth.php` configuration file. To learn more about writing custom user providers, consult the [authentication documentation](/docs/{{version}}/authentication#adding-custom-user-providers) + > {tip} When manually implementing password resets, you are required to define the contents of the views and routes yourself. If you would like scaffolding that includes all necessary authentication and verification logic, check out the [Laravel application starter kits](/docs/{{version}}/starter-kits). <a name="resetting-the-password"></a> From 4a2edb40199497550017859b20f25f1213cf1473 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 16:42:05 -0600 Subject: [PATCH 146/274] wip --- passwords.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/passwords.md b/passwords.md index d92546460a..86065586b2 100644 --- a/passwords.md +++ b/passwords.md @@ -86,9 +86,9 @@ Next, we will define the routes necessary to actually reset the password once th Route::get('/reset-password/{token}', function ($token) { return view('auth.reset-password', ['token' => $token]); - })->middleware(['guest'])->name('password.reset'); + })->middleware('guest')->name('password.reset'); -The view that is returned by this route should have a form containing an `email` field, a `password` field, a `password_confirmation` field, and a hidden `token` field, which should contain the value of the secret token received by our route. +The view that is returned by this route should display a form containing an `email` field, a `password` field, a `password_confirmation` field, and a hidden `token` field, which should contain the value of the secret `$token` received by our route. <a name="password-reset-handling-the-form-submission"></a> #### Handling The Form Submission @@ -123,49 +123,57 @@ Of course, we need to define a route to actually handle the password reset form return $status == Password::PASSWORD_RESET ? redirect()->route('login')->with('status', __($status)) - : back()->withErrors(['email' => __($status)]); - })->middleware(['guest'])->name('password.update'); + : back()->withErrors(['email' => [__($status)]]); + })->middleware('guest')->name('password.update'); Before moving on, let's examine this route in more detail. First, the request's `token`, `email`, and `password` attributes are validated. Next, we will use Laravel's built-in "password broker" (via the `Password` facade) to validate the password reset request credentials. -If the token, email address, and password given to the password broker are valid, the closure passed to the `reset` method will be invoked. Within this closure, which receives the user instance and the plain-text password, we may update the user's password in the database. +If the token, email address, and password given to the password broker are valid, the closure passed to the `reset` method will be invoked. Within this closure, which receives the user instance and the plain-text password provided to the password reset form, we may update the user's password in the database. The `reset` method returns a "status" slug. This status may be translated using Laravel's [localization](/docs/{{version}}/localization) helpers in order to display a user-friendly message to the user regarding the status of their request. The translation of the password reset status is determined by your application's `resources/lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file. +Before moving on, you may wondering how Laravel knows how to retrieve the user record from your application's database when calling the `Password` facade's `reset` method. The Laravel password broker utilizes your authentication system's "user providers" to retrieve database records. The user provider used by the password broker is configured within the `passwords` configuration array of your `config/auth.php` configuration file. To learn more about writing custom user providers, consult the [authentication documentation](/docs/{{version}}/authentication#adding-custom-user-providers) + <a name="password-customization"></a> ## Customization <a name="reset-link-customization"></a> #### Reset Link Customization -You may customize the password reset link URL using the `createUrlUsing` method provided by the `ResetPassword` notification class. This method accepts a closure which receives the user instance that is receiving the notification as well as the password reset link token. Typically, you should call this method from a service provider's `boot` method: +You may customize the password reset link URL using the `createUrlUsing` method provided by the `ResetPassword` notification class. This method accepts a closure which receives the user instance that is receiving the notification as well as the password reset link token. Typically, you should call this method from your `App\Providers\AuthServiceProvider` service provider's `boot` method: use Illuminate\Auth\Notifications\ResetPassword; /** - * Bootstrap any application services. + * Register any authentication / authorization services. * * @return void */ public function boot() { - ResetPassword::createUrlUsing(function ($notifiable, string $token) { - return 'https://example.com/auth/reset-password?token='.$token; + $this->registerPolicies(); + + ResetPassword::createUrlUsing(function ($user, string $token) { + return 'https://example.com/reset-password?token='.$token; }); } <a name="reset-email-customization"></a> #### 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: +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 `App\Models\User` model. Within this method, you may send the notification using any [notification class](/docs/{{version}}/notifications) of your own creation. The password reset `$token` is the first argument received by the method. You may use this `$token` to build the password reset URL of your choice and send your notification to the user: + + use App\Notifications\ResetPasswordNotification; /** - * Send the password reset notification. + * Send a password reset notification to the user. * * @param string $token * @return void */ public function sendPasswordResetNotification($token) { - $this->notify(new ResetPasswordNotification($token)); + $url = 'https://example.com/reset-password?token='.$token; + + $this->notify(new ResetPasswordNotification($url)); } From 055c9335f4e94c8712b7d03dfad076592529532a Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 18:35:06 -0600 Subject: [PATCH 147/274] wip --- database.md | 6 +++--- installation.md | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/database.md b/database.md index 18b0dda7a5..6c564ccdba 100644 --- a/database.md +++ b/database.md @@ -11,7 +11,7 @@ <a name="introduction"></a> ## Introduction -Laravel makes interacting with databases extremely simple across a variety of database backends using either raw SQL, the [fluent query builder](/docs/{{version}}/queries), and the [Eloquent ORM](/docs/{{version}}/eloquent). Currently, Laravel supports four databases: +Almost every modern web application interacts with a database. Laravel makes interacting with databases extremely simple across a variety of supported databases using raw SQL, a [fluent query builder](/docs/{{version}}/queries), and the [Eloquent ORM](/docs/{{version}}/eloquent). Currently, Laravel provides first-party support for four databases: <div class="content-list" markdown="1"> - MySQL 5.6+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) @@ -23,9 +23,9 @@ Laravel makes interacting with databases extremely simple across a variety of da <a name="configuration"></a> ### Configuration -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. +The configuration for Laravel's database services is located in your application's `config/database.php` configuration file. 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 Laravel's supported database systems are provided in this file. -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. 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 Sail](/docs/{{version}}/installation#laravel-sail), which is a convenient Docker configuration for doing Laravel development on your local machine. However, you are free to modify this configuration as needed for your local database. <a name="sqlite-configuration"></a> #### SQLite Configuration diff --git a/installation.md b/installation.md index 731f2da1a7..f637c3d9de 100644 --- a/installation.md +++ b/installation.md @@ -14,6 +14,7 @@ - [Executing Commands](#executing-sail-commands) - [Interacting With Databases](#interacting-with-sail-databases) - [Running Tests](#running-tests) + - [Previewing Emails](#previewing-emails) - [Adding Additional Services](#adding-additional-sail-services) - [Container CLI](#sail-container-cli) - [Customization](#sail-customization) @@ -288,6 +289,13 @@ Finally, you may run your Dusk test suite by starting Sail and running the `dusk ./sail dusk +<a name="previewing-emails"></a> +### Previewing Emails + +Laravel Sail's default `docker-compose.yml` file contains a service entry for [MailHog](https://github.com/mailhog/MailHog). MailHog intercepts emails sent by your application during local development and provides a convenient web interface so that you can preview your email messages in your browser. + +When Sail is running, you may access the MailHog web interface at: `http://localhost:8025`. MailHog's default SMTP port is `1025`. + <a name="adding-additional-sail-services"></a> ### Adding Additional Services From cc774b5b6cd10dbffab72e2f3d9ec5fed429688b Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 19:02:07 -0600 Subject: [PATCH 148/274] wip --- database.md | 105 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/database.md b/database.md index 6c564ccdba..ea92266c1a 100644 --- a/database.md +++ b/database.md @@ -3,9 +3,9 @@ - [Introduction](#introduction) - [Configuration](#configuration) - [Read & Write Connections](#read-and-write-connections) +- [Running SQL Queries](#running-queries) - [Using Multiple Database Connections](#using-multiple-database-connections) -- [Running Raw SQL Queries](#running-queries) -- [Listening For Query Events](#listening-for-query-events) + - [Listening For Query Events](#listening-for-query-events) - [Database Transactions](#database-transactions) <a name="introduction"></a> @@ -23,14 +23,14 @@ Almost every modern web application interacts with a database. Laravel makes int <a name="configuration"></a> ### Configuration -The configuration for Laravel's database services is located in your application's `config/database.php` configuration file. 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 Laravel's supported database systems are provided in this file. +The configuration for Laravel's database services is located in your application's `config/database.php` configuration file. In this file you may define all of your database connections, as well as specify which connection should be used by default. Most of the configuration options within this file are driven by the values of your application's environment variables. Examples for most of Laravel's supported database systems are provided in this file. -By default, Laravel's sample [environment configuration](/docs/{{version}}/configuration#environment-configuration) is ready to use with [Laravel Sail](/docs/{{version}}/installation#laravel-sail), which is a convenient Docker configuration for doing Laravel development on your local machine. However, 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 Sail](/docs/{{version}}/installation#laravel-sail), which is a Docker configuration for developing Laravel applications on your local machine. However, you are free to modify your database configuration as needed for your local database. <a name="sqlite-configuration"></a> #### SQLite Configuration -After creating a new SQLite database using a command such as `touch database/database.sqlite`, you can easily configure your environment variables to point to this newly created database by using the database's absolute path: +SQLite databases are contained within a single file on your filesystem. You can create a new SQLite database using the `touch` command in your terminal: `touch database/database.sqlite`. After the database has been created, you may easily configure your environment variables to point to this database by placing the absolute path to the database in the `DB_DATABASE` environment variable: DB_CONNECTION=sqlite DB_DATABASE=/absolute/path/to/database.sqlite @@ -44,13 +44,17 @@ To enable foreign key constraints for SQLite connections, you should set the `DB Typically, database connections are configured using multiple configuration values such as `host`, `database`, `username`, `password`, etc. Each of these configuration values has its own corresponding environment variable. This means that when configuring your database connection information on a production server, you need to manage several environment variables. -Some managed database providers such as Heroku provide a single database "URL" that contains all of the connection information for the database in a single string. An example database URL may look something like the following: +Some managed database providers such as AWS and Heroku provide a single database "URL" that contains all of the connection information for the database in a single string. An example database URL may look something like the following: - mysql://root:password@127.0.0.1/forge?charset=UTF-8 +```html +mysql://root:password@127.0.0.1/forge?charset=UTF-8 +``` These URLs typically follow a standard schema convention: - driver://username:password@host:port/database?options +```html +driver://username:password@host:port/database?options +``` For convenience, Laravel supports these URLs as an alternative to configuring your database with multiple configuration options. If the `url` (or corresponding `DATABASE_URL` environment variable) configuration option is present, it will be used to extract the database connection and credential information. @@ -83,35 +87,24 @@ To see how read / write connections should be configured, let's look at this exa 'prefix' => '', ], -Note that three keys have been added to the configuration array: `read`, `write` and `sticky`. The `read` and `write` keys have array values containing a single key: `host`. The rest of the database options for the `read` and `write` connections will be merged from the main `mysql` array. +Note that three keys have been added to the configuration array: `read`, `write` and `sticky`. The `read` and `write` keys have array values containing a single key: `host`. The rest of the database options for the `read` and `write` connections will be merged from the main `mysql` configuration array. -You only need to place items in the `read` and `write` arrays if you wish to override the values from the main array. So, in this case, `192.168.1.1` will be used as the host for the "read" connection, while `192.168.1.3` will be used for the "write" connection. The database credentials, prefix, character set, and all other options in the main `mysql` array will be shared across both connections. +You only need to place items in the `read` and `write` arrays if you wish to override the values from the main `mysql` array. So, in this case, `192.168.1.1` will be used as the host for the "read" connection, while `192.168.1.3` will be used for the "write" connection. The database credentials, prefix, character set, and all other options in the main `mysql` array will be shared across both connections. When multiple values exist in the `host` configuration array, a database host will be randomly chosen for each request. <a name="the-sticky-option"></a> #### The `sticky` Option The `sticky` option is an *optional* value that can be used to allow the immediate reading of records that have been written to the database during the current request cycle. If the `sticky` option is enabled and a "write" operation has been performed against the database during the current request cycle, any further "read" operations will use the "write" connection. This ensures that any data written during the request cycle can be immediately read back from the database during that same request. It is up to you to decide if this is the desired behavior for your application. -<a name="using-multiple-database-connections"></a> -### Using Multiple Database Connections - -When using multiple connections, you may access each connection via the `connection` method on the `DB` facade. The `name` passed to the `connection` method should correspond to one of the connections listed in your `config/database.php` configuration file: - - $users = DB::connection('foo')->select(...); - -You may also access the raw, underlying PDO instance using the `getPdo` method on a connection instance: - - $pdo = DB::connection()->getPdo(); - <a name="running-queries"></a> -## Running Raw SQL Queries +## Running SQL Queries Once you have configured your database connection, you may run queries using the `DB` facade. The `DB` facade provides methods for each type of query: `select`, `update`, `insert`, `delete`, and `statement`. <a name="running-a-select-query"></a> #### Running A Select Query -To run a basic query, you may use the `select` method on the `DB` facade: +To run a basic SELECT query, you may use the `select` method on the `DB` facade: <?php @@ -125,7 +118,7 @@ To run a basic query, you may use the `select` method on the `DB` facade: /** * Show a list of all of the application's users. * - * @return Response + * @return \Illuminate\Http\Response */ public function index() { @@ -135,9 +128,13 @@ To run a basic query, you may use the `select` method on the `DB` facade: } } -The first argument passed to the `select` method is the raw SQL query, while the second argument is any parameter bindings that need to be bound to the query. Typically, these are the values of the `where` clause constraints. Parameter binding provides protection against SQL injection. +The first argument passed to the `select` method is the SQL query, while the second argument is any parameter bindings that need to be bound to the query. Typically, these are the values of the `where` clause constraints. Parameter binding provides protection against SQL injection. -The `select` method will always return an `array` of results. Each result within the array will be a PHP `stdClass` object, allowing you to access the values of the results: +The `select` method will always return an `array` of results. Each result within the array will be a PHP `stdClass` object representing a record from the database: + + use Illuminate\Support\Facades\DB; + + $users = DB::select('select * from users'); foreach ($users as $user) { echo $user->name; @@ -153,21 +150,30 @@ Instead of using `?` to represent your parameter bindings, you may execute a que <a name="running-an-insert-statement"></a> #### Running An Insert Statement -To execute an `insert` statement, you may use the `insert` method on the `DB` facade. Like `select`, this method takes the raw SQL query as its first argument and bindings as its second argument: +To execute an `insert` statement, you may use the `insert` method on the `DB` facade. Like `select`, this method accepts the SQL query as its first argument and bindings as its second argument: + + use Illuminate\Support\Facades\DB; - DB::insert('insert into users (id, name) values (?, ?)', [1, 'Dayle']); + DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']); <a name="running-an-update-statement"></a> #### Running An Update Statement -The `update` method should be used to update existing records in the database. The number of rows affected by the statement will be returned: +The `update` method should be used to update existing records in the database. The number of rows affected by the statement is returned by the method: - $affected = DB::update('update users set votes = 100 where name = ?', ['John']); + use Illuminate\Support\Facades\DB; + + $affected = DB::update( + 'update users set votes = 100 where name = ?', + ['Anita'] + ); <a name="running-a-delete-statement"></a> #### Running A Delete Statement -The `delete` method should be used to delete records from the database. Like `update`, the number of rows affected will be returned: +The `delete` method should be used to delete records from the database. Like `update`, the number of rows affected will be returned by the method: + + use Illuminate\Support\Facades\DB; $deleted = DB::delete('delete from users'); @@ -178,10 +184,23 @@ Some database statements do not return any value. For these types of operations, DB::statement('drop table users'); +<a name="using-multiple-database-connections"></a> +### Using Multiple Database Connections + +If your application defines multiple connections in your `config/database.php` configuration file, you may access each connection via the `connection` method provided by the `DB` facade. The connection name passed to the `connection` method should correspond to one of the connections listed in your `config/database.php` configuration file: + + use Illuminate\Support\Facades\DB; + + $users = DB::connection('sqlite')->select(...); + +You may access the raw, underlying PDO instance of a connection using the `getPdo` method on a connection instance: + + $pdo = DB::connection()->getPdo(); + <a name="listening-for-query-events"></a> -## Listening For Query Events +### Listening For Query Events -If you would like to receive each SQL query executed by your application, you may use the `listen` method. This method is useful for logging queries or debugging. You may register your query listener in a [service provider](/docs/{{version}}/providers): +If you would like to specify a closure that is invoked for each SQL query executed by your application, you may use the `DB` facade's `listen` method. This method can be useful for logging queries or debugging. You may register your query listener closure in the `boot` method of a [service provider](/docs/{{version}}/providers): <?php @@ -220,29 +239,35 @@ If you would like to receive each SQL query executed by your application, you ma <a name="database-transactions"></a> ## Database Transactions -You may use the `transaction` method on the `DB` facade to run a set of operations within a database transaction. If an exception is thrown within the transaction closure, the transaction will automatically be rolled back. If the closure executes successfully, the transaction will automatically be committed. You don't need to worry about manually rolling back or committing while using the `transaction` method: +You may use the `transaction` method provided by the `DB` facade to run a set of operations within a database transaction. If an exception is thrown within the transaction closure, the transaction will automatically be rolled back. If the closure executes successfully, the transaction will automatically be committed. You don't need to worry about manually rolling back or committing while using the `transaction` method: + + use Illuminate\Support\Facades\DB; DB::transaction(function () { - DB::table('users')->update(['votes' => 1]); + DB::update('update users set votes = 1'); - DB::table('posts')->delete(); + DB::delete('delete from posts'); }); <a name="handling-deadlocks"></a> #### 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: +The `transaction` method accepts an optional second argument which defines the number of times a transaction should be retried when a deadlock occurs. Once these attempts have been exhausted, an exception will be thrown: + + use Illuminate\Support\Facades\DB; DB::transaction(function () { - DB::table('users')->update(['votes' => 1]); + DB::update('update users set votes = 1'); - DB::table('posts')->delete(); + DB::delete('delete from posts'); }, 5); <a name="manually-using-transactions"></a> #### 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: +If you would like to begin a transaction manually and have complete control over rollbacks and commits, you may use the `beginTransaction` method provided by the `DB` facade: + + use Illuminate\Support\Facades\DB; DB::beginTransaction(); From e59a31a3e8d8def0cd5158207379d9c007fa3df4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell <taylorotwell@gmail.com> Date: Fri, 20 Nov 2020 19:17:12 -0600 Subject: [PATCH 149/274] wip --- blade.md | 340 +++++++++++++++++++++++++++++++------------------------ views.md | 16 +-- 2 files changed, 203 insertions(+), 153 deletions(-) diff --git a/blade.md b/blade.md index a035049fe7..d2dce82a55 100644 --- a/blade.md +++ b/blade.md @@ -230,21 +230,25 @@ Or, you may determine if the application is running in a specific environment us You may determine if a template inheritance section has content using the `@hasSection` directive: - @hasSection('navigation') - <div class="pull-right"> - @yield('navigation') - </div> +```html +@hasSection('navigation') + <div class="pull-right"> + @yield('navigation') + </div> - <div class="clearfix"></div> - @endif + <div class="clearfix"></div> +@endif +``` You may use the `sectionMissing` directive to determine if a section does not have content: - @sectionMissing('navigation') - <div class="pull-right"> - @include('default-navigation') - </div> - @endif +```html +@sectionMissing('navigation') + <div class="pull-right"> + @include('default-navigation') + </div> +@endif +``` <a name="switch-statements"></a> ### Switch Statements @@ -369,13 +373,15 @@ Blade also allows you to define comments in your views. However, unlike HTML com Blade's `@include` directive allows you to include a Blade view from within another view. All variables that are available to the parent view will be made available to the included view: - <div> - @include('shared.errors') +```html +<div> + @include('shared.errors') - <form> - <!-- Form Contents --> - </form> - </div> + <form> + <!-- Form Contents --> + </form> +</div> +``` Even though the included view will inherit all data available in the parent view, you may also pass an array of additional data that should be made available to the included view: @@ -438,45 +444,51 @@ Most web applications maintain the same general layout across various pages. It For example, imagine we are building a "todo" list application. We might define a `layout` component that looks like the following: - <!-- resources/views/components/layout.blade.php --> +```html +<!-- resources/views/components/layout.blade.php --> - <html> - <head> - <title>{{ $title ?? 'Todo Manager' }} - </head> - <body> - <h1>Todos</h1> - <hr/> - {{ $slot }} - </body> - </html> +<html> + <head> + <title>{{ $title ?? 'Todo Manager' }} + </head> + <body> + <h1>Todos</h1> + <hr/> + {{ $slot }} + </body> +</html> +``` <a name="applying-the-layout-component"></a> #### Applying The Layout Component Once the `layout` component has been defined, we may create a Blade view that utilizes the component. In this example, we will define a simple view that displays our task list: - <!-- resources/views/tasks.blade.php --> +```html +<!-- resources/views/tasks.blade.php --> - <x-layout> - @foreach ($tasks as $task) - {{ $task }} - @endforeach - </x-layout> +<x-layout> + @foreach ($tasks as $task) + {{ $task }} + @endforeach +</x-layout> +``` Remember, content that is injected into a component will be supplied to the default `$slot` variable within our `layout` component. As you may have noticed, our `layout` also respects a `$title` slot if one is provided; otherwise, a default title is shown. We may inject a custom title from our task list view using the standard slot syntax discussed in the [component documentation](#components): - <!-- resources/views/tasks.blade.php --> +```html +<!-- resources/views/tasks.blade.php --> - <x-layout> - <x-slot name="title"> - Custom Title - </x-slot> +<x-layout> + <x-slot name="title"> + Custom Title + </x-slot> - @foreach ($tasks as $task) - {{ $task }} - @endforeach - </x-layout> + @foreach ($tasks as $task) + {{ $task }} + @endforeach +</x-layout> +``` Now that we have defined our layout and task list views, we just need to return the `task` view from a route: @@ -496,22 +508,24 @@ Layouts may also be created via "template inheritance". This was the primary way To get started, let's take a look at a simple example. First, we will examine a page layout. Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Blade view: - <!-- resources/views/layouts/app.blade.php --> +```html +<!-- resources/views/layouts/app.blade.php --> - <html> - <head> - <title>App Name - @yield('title') - - - @section('sidebar') - This is the master sidebar. - @show + + + App Name - @yield('title') + + + @section('sidebar') + This is the master sidebar. + @show -
- @yield('content') -
- - +
+ @yield('content') +
+ + +``` As you can see, this file contains typical HTML mark-up. However, take note of the `@section` and `@yield` directives. The `@section` directive, as the name implies, defines a section of content, while the `@yield` directive is used to display the contents of a given section. @@ -522,21 +536,23 @@ Now that we have defined a layout for our application, let's define a child page When defining a child view, use the `@extends` Blade directive to specify which layout the child view should "inherit". Views which extend a Blade layout may inject content into the layout's sections using `@section` directives. Remember, as seen in the example above, the contents of these sections will be displayed in the layout using `@yield`: - +```html + - @extends('layouts.app') +@extends('layouts.app') - @section('title', 'Page Title') +@section('title', 'Page Title') - @section('sidebar') - @@parent +@section('sidebar') + @@parent -

This is appended to the master sidebar.

- @endsection +

This is appended to the master sidebar.

+@endsection - @section('content') -

This is my body content.

- @endsection +@section('content') +

This is my body content.

+@endsection +``` In this example, the `sidebar` section is utilizing the `@@parent` directive to append (rather than overwriting) content to the layout's sidebar. The `@@parent` directive will be replaced by the content of the layout when the view is rendered. @@ -554,49 +570,57 @@ The `@yield` directive also accepts a default value as its second parameter. Thi Anytime you define an HTML form in your application, you should include a hidden CSRF token field in the form so that [the CSRF protection](https://laravel.com/docs/{{version}}/csrf) middleware can validate the request. You may use the `@csrf` Blade directive to generate the token field: -
- @csrf +```html + + @csrf - ... -
+ ... + +``` ### Method Field 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` Blade directive can create this field for you: -
- @method('PUT') +```html + + @method('PUT') - ... -
+ ... + +``` ### Validation Errors The `@error` directive may be used to quickly check if [validation error messages](/docs/{{version}}/validation#quick-displaying-the-validation-errors) exist for a given attribute. Within an `@error` directive, you may echo the `$message` variable to display the error message: - +```html + - + - + - @error('title') -
{{ $message }}
- @enderror +@error('title') +
{{ $message }}
+@enderror +``` You may pass [the name of a specific error bag](/docs/{{version}}/validation#named-error-bags) as the second parameter to the `@error` directive to retrieve validation error messages on pages containing multiple forms: - +```html + - + - + - @error('email', 'login') -
{{ $message }}
- @enderror +@error('email', 'login') +
{{ $message }}
+@enderror +``` ### Raw PHP @@ -688,9 +712,11 @@ You should define the component's required data in its class constructor. All pu When your component is rendered, you may display the contents of your component's public variables by echoing the variables by name: -
- {{ $message }} -
+```html +
+ {{ $message }} +
+``` #### Casing @@ -811,9 +837,11 @@ If we assume this component is utilized like so: The final, rendered HTML of the component will appear like the following: -
- -
+```html +
+ +
+``` #### Non-Class Attribute Merging @@ -862,50 +890,60 @@ Using the `first` method, you may render the first attribute in a given attribut You will often need to pass additional content to your component via "slots". Component slots are rendered by echoing the `$slot` variable. To explore this concept, let's imagine that an `alert` component has the following markup: - +```html + -
- {{ $slot }} -
+
+ {{ $slot }} +
+``` We may pass content to the `slot` by injecting content into the component: - - Whoops! Something went wrong! - +```html + + Whoops! Something went wrong! + +``` Sometimes a component may need to render multiple different slots in different locations within the component. Let's modify our alert component to allow for the injection of a "title" slot: - +```html + - {{ $title }} +{{ $title }} -
- {{ $slot }} -
+
+ {{ $slot }} +
+``` You may define the content of the named slot using the `x-slot` tag. Any content not within an explicit `x-slot` tag will be passed to the component in the `$slot` variable: - - - Server Error - +```html + + + Server Error + - Whoops! Something went wrong! - + Whoops! Something went wrong! + +``` #### Scoped Slots If you have used a JavaScript framework such as Vue, you may be familiar with "scoped slots", which allow you to access data or methods from the component within your slot. You may achieve similar behavior in Laravel by defining public methods or properties on your component and accessing the component within your slot via the `$component` variable. In this example, we will assume that the `x-alert` component has a public `formatAlert` method defined on its component class: - - - {{ $component->formatAlert('Server Error') }} - +```html + + + {{ $component->formatAlert('Server Error') }} + - Whoops! Something went wrong! - + Whoops! Something went wrong! + +``` ### Inline Component Views @@ -1024,40 +1062,48 @@ Blade will automatically detect the class that's linked to this component by pas Blade allows you to push to named stacks which can be rendered somewhere else in another view or layout. This can be particularly useful for specifying any JavaScript libraries required by your child views: - @push('scripts') - - @endpush +```html +@push('scripts') + +@endpush +``` You may push to a stack as many times as needed. To render the complete stack contents, pass the name of the stack to the `@stack` directive: - - +```html + + - @stack('scripts') - + @stack('scripts') + +``` If you would like to prepend content onto the beginning of a stack, you should use the `@prepend` directive: - @push('scripts') - This will be second... - @endpush +```html +@push('scripts') + This will be second... +@endpush - // Later... +// Later... - @prepend('scripts') - This will be first... - @endprepend +@prepend('scripts') + This will be first... +@endprepend +``` ## Service Injection The `@inject` directive may be used to retrieve a service from the Laravel [service container](/docs/{{version}}/container). The first argument passed to `@inject` is the name of the variable the service will be placed into, while the second argument is the class or interface name of the service you wish to resolve: - @inject('metrics', 'App\Services\MetricsService') +```html +@inject('metrics', 'App\Services\MetricsService') -
- Monthly Revenue: {{ $metrics->monthlyRevenue() }}. -
+
+ Monthly Revenue: {{ $metrics->monthlyRevenue() }}. +
+``` ## Extending Blade @@ -1125,14 +1171,16 @@ Programming a custom directive is sometimes more complex than necessary when def Once the custom conditional has been defined, you can use it within your templates: - @disk('local') - // The application is using the local disk... - @elsedisk('s3') - // The application is using the s3 disk... - @else - // The application is using some other disk... - @enddisk - - @unlessdisk('local') - // The application is not using the local disk... - @enddisk +```html +@disk('local') + +@elsedisk('s3') + +@else + +@enddisk + +@unlessdisk('local') + +@enddisk +``` diff --git a/views.md b/views.md index bfe0bce8a9..9ee9c0abd7 100644 --- a/views.md +++ b/views.md @@ -16,13 +16,15 @@ Of course, it's not practical to return entire HTML documents strings directly from your routes and controllers. Thankfully, views provide a convenient way to place all of our HTML in separate files. Views separate your controller / application logic from your presentation logic and are stored in the `resources/views` directory. A simple view might look something like this: - - - - -

Hello, {{ $name }}

- - +```html + + + + +

Hello, {{ $name }}

+ + +``` Since this view is stored at `resources/views/greeting.blade.php`, we may return it using the global `view` helper like so: From edb23d983273ff36c28024698b738c59cb74cb2d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 20 Nov 2020 19:37:35 -0600 Subject: [PATCH 150/274] wip --- queries.md | 62 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/queries.md b/queries.md index 28324fc3da..beca524f54 100644 --- a/queries.md +++ b/queries.md @@ -1,7 +1,7 @@ # Database: Query Builder - [Introduction](#introduction) -- [Retrieving Results](#retrieving-results) +- [Running Database Queries](#running-database-queries) - [Chunking Results](#chunking-results) - [Aggregates](#aggregates) - [Selects](#selects) @@ -26,19 +26,19 @@ ## Introduction -Laravel's database query builder provides a convenient, fluent interface to creating and running database queries. It can be used to perform most database operations in your application and works on all supported database systems. +Laravel's database query builder provides a convenient, fluent interface to creating and running database queries. It can be used to perform most database operations in your application and works perfectly with all of Laravel's supported database systems. -The Laravel query builder uses PDO parameter binding to protect your application against SQL injection attacks. There is no need to clean strings being passed as bindings. +The Laravel query builder uses PDO parameter binding to protect your application against SQL injection attacks. There is no need to clean or sanitize strings passed to the query builder as query bindings. -> {note} PDO does not support binding column names. Therefore, you should never allow user input to dictate the column names referenced by your queries, including "order by" columns, etc. If you must allow the user to select certain columns to query against, always validate the column names against a white-list of allowed columns. +> {note} PDO does not support binding column names. Therefore, you should never allow user input to dictate the column names referenced by your queries, including "order by" columns. - -## Retrieving Results + +## Running Database Queries #### Retrieving All Rows From A Table -You may use the `table` method on the `DB` facade to begin a query. The `table` method returns a fluent query builder instance for the given table, allowing you to chain more constraints onto the query and then finally get the results using the `get` method: +You may use the `table` method provided by the `DB` facade to begin a query. The `table` method returns a fluent query builder instance for the given table, allowing you to chain more constraints onto the query and then finally retrieve the results of the query using the `get` method: get(); foreach ($users as $user) { echo $user->name; } +> {tip} Laravel collections provide a variety of extremely powerful methods for mapping and reducing data. For more information on Laravel colletions, check out the [collection documentation](/docs/{{version}}/collections). + #### Retrieving A Single Row / Column From A Table -If you just need to retrieve a single row from the database table, you may use the `first` method. This method will return a single `stdClass` object: +If you just need to retrieve a single row from a database table, you may use the `DB` facade's `first` method. This method will return a single `stdClass` object: $user = DB::table('users')->where('name', 'John')->first(); - echo $user->name; + return $user->email; -If you don't even need an entire row, you may extract a single value from a record using the `value` method. This method will return the value of the column directly: +If you don't need an entire row, you may extract a single value from a record using the `value` method. This method will return the value of the column directly: $email = DB::table('users')->where('name', 'John')->value('email'); @@ -88,26 +94,30 @@ To retrieve a single row by its `id` column value, use the `find` method: #### Retrieving A List Of Column Values -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: +If you would like to retrieve an `Illuminate\Support\Collection` instance containing the values of a single column, you may use the `pluck` method. In this example, we'll retrieve a collection of role titles: + + use Illuminate\Support\Facades\DB; - $titles = DB::table('roles')->pluck('title'); + $titles = DB::table('users')->pluck('title'); foreach ($titles as $title) { echo $title; } - You may also specify a custom key column for the returned Collection: + You may specify the column that the resulting collection should use as its keys by providing a second argument to the `pluck` method: - $roles = DB::table('roles')->pluck('title', 'name'); + $titles = DB::table('users')->pluck('title', 'name'); - foreach ($roles as $name => $title) { + foreach ($titles as $name => $title) { echo $title; } ### Chunking Results -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: +If you need to work with thousands of database records, consider using the `chunk` method provided by the `DB` facade. This method retrieves a small chunk of results at a time and feeds each chunk into a closure for processing. For example, let's retrieve the entire `users` table in chunks of 100 records at a time: + + use Illuminate\Support\Facades\DB; DB::table('users')->orderBy('id')->chunk(100, function ($users) { foreach ($users as $user) { @@ -123,7 +133,7 @@ You may stop further chunks from being processed by returning `false` from the c return false; }); -If you are updating database records while chunking results, your chunk results could change in unexpected ways. So, when updating records while chunking, it is always best to use the `chunkById` method instead. This method will automatically paginate the results based on the record's primary key: +If you are updating database records while chunking results, your chunk results could change in unexpected ways. If you plan to update the retrieved records while chunking, it is always best to use the `chunkById` method instead. This method will automatically paginate the results based on the record's primary key: DB::table('users')->where('active', false) ->chunkById(100, function ($users) { @@ -139,13 +149,15 @@ If you are updating database records while chunking results, your chunk results ### Aggregates -The query builder also provides a variety of aggregate methods such as `count`, `max`, `min`, `avg`, and `sum`. You may call any of these methods after constructing your query: +The query builder also provides a variety of methods for retrieving aggregate values like `count`, `max`, `min`, `avg`, and `sum`. You may call any of these methods after constructing your query: + + use Illuminate\Support\Facades\DB; $users = DB::table('users')->count(); $price = DB::table('orders')->max('price'); -You may combine these methods with other clauses: +Of course, you may combine these methods with other clauses to fine-tune how your aggregate value is calculated: $price = DB::table('orders') ->where('finalized', 1) @@ -156,9 +168,13 @@ You may combine these methods with other clauses: Instead of using the `count` method to determine if any records exist that match your query's constraints, you may use the `exists` and `doesntExist` methods: - return DB::table('orders')->where('finalized', 1)->exists(); + if (DB::table('orders')->where('finalized', 1)->exists()) { + // ... + } - return DB::table('orders')->where('finalized', 1)->doesntExist(); + if (DB::table('orders')->where('finalized', 1)->doesntExist()) { + // ... + } ## Selects From c64ce30be4c761dd9670a879c3a941174bb83415 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 20 Nov 2020 19:49:59 -0600 Subject: [PATCH 151/274] wip --- queries.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/queries.md b/queries.md index beca524f54..7274aaf0fc 100644 --- a/queries.md +++ b/queries.md @@ -184,6 +184,8 @@ Instead of using the `count` method to determine if any records exist that match You may not always want to select all columns from a database table. Using the `select` method, you can specify a custom `select` clause for the query: + use Illuminate\Support\Facades\DB; + $users = DB::table('users')->select('name', 'email as user_email')->get(); The `distinct` method allows you to force the query to return distinct results: @@ -199,20 +201,20 @@ If you already have a query builder instance and you wish to add a column to its ## Raw Expressions -Sometimes you may need to use a raw expression in a query. To create a raw expression, you may use the `DB::raw` method: +Sometimes you may need to insert an arbitrary string into a query. To create a raw string expression, you may use the `raw` method provided by the `DB` facade: $users = DB::table('users') - ->select(DB::raw('count(*) as user_count, status')) - ->where('status', '<>', 1) - ->groupBy('status') - ->get(); + ->select(DB::raw('count(*) as user_count, status')) + ->where('status', '<>', 1) + ->groupBy('status') + ->get(); -> {note} Raw statements will be injected into the query as strings, so you should be extremely careful to not create SQL injection vulnerabilities. +> {note} Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities. ### Raw Methods -Instead of using `DB::raw`, you may also use the following methods to insert a raw expression into various parts of your query. +Instead of using the `DB::raw` method, you may also use the following methods to insert a raw expression into various parts of your query. **Remember, Laravel can not guarantee that any query using raw expressions is protected against SQL injection vulnerabilities.** #### `selectRaw` @@ -235,7 +237,7 @@ The `whereRaw` and `orWhereRaw` methods can be used to inject a raw `where` clau #### `havingRaw / orHavingRaw` -The `havingRaw` and `orHavingRaw` methods may be used to set a raw string as the value of the `having` clause. These methods accept an optional array of bindings as their second argument: +The `havingRaw` and `orHavingRaw` methods may be used to provide a raw string as the value of the `having` clause. These methods accept an optional array of bindings as their second argument: $orders = DB::table('orders') ->select('department', DB::raw('SUM(price) as total_sales')) @@ -246,7 +248,7 @@ The `havingRaw` and `orHavingRaw` methods may be used to set a raw string as the #### `orderByRaw` -The `orderByRaw` method may be used to set a raw string as the value of the `order by` clause: +The `orderByRaw` method may be used to provide a raw string as the value of the `order by` clause: $orders = DB::table('orders') ->orderByRaw('updated_at - created_at DESC') @@ -255,7 +257,7 @@ The `orderByRaw` method may be used to set a raw string as the value of the `ord ### `groupByRaw` -The `groupByRaw` method may be used to set a raw string as the value of the `group by` clause: +The `groupByRaw` method may be used to provide a raw string as the value of the `group by` clause: $orders = DB::table('orders') ->select('city', 'state') From 5453a9cda2fb899d74baa5c25bc6e89825d8b758 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 20 Nov 2020 20:06:04 -0600 Subject: [PATCH 152/274] wip --- queries.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/queries.md b/queries.md index 7274aaf0fc..95eaededf5 100644 --- a/queries.md +++ b/queries.md @@ -270,7 +270,9 @@ The `groupByRaw` method may be used to provide a raw string as the value of the #### Inner Join Clause -The query builder may also be used to write join statements. To perform a basic "inner join", you may use the `join` method on a query builder instance. The first argument passed to the `join` method is the name of the table you need to join to, while the remaining arguments specify the column constraints for the join. You can even join to multiple tables in a single query: +The query builder may also be used to add join clauses to your queries. To perform a basic "inner join", you may use the `join` method on a query builder instance. The first argument passed to the `join` method is the name of the table you need to join to, while the remaining arguments specify the column constraints for the join. You may even join to multiple tables in a single query: + + use Illuminate\Support\Facades\DB; $users = DB::table('users') ->join('contacts', 'users.id', '=', 'contacts.user_id') @@ -294,7 +296,7 @@ If you would like to perform a "left join" or "right join" instead of an "inner #### Cross Join Clause -To perform a "cross join" use the `crossJoin` method with the name of the table you wish to cross join to. Cross joins generate a cartesian product between the first table and the joined table: +You may use the `crossJoin` method to perform a "cross join". Cross joins generate a cartesian product between the first table and the joined table: $sizes = DB::table('sizes') ->crossJoin('colors') @@ -303,7 +305,7 @@ To perform a "cross join" use the `crossJoin` method with the name of the table #### Advanced Join Clauses -You may also specify more advanced join clauses. To get started, pass a closure as the second argument into the `join` method. The closure will receive a `JoinClause` object which allows you to specify constraints on the `join` clause: +You may also specify more advanced join clauses. To get started, pass a closure as the second argument to the `join` method. The closure will receive a `Illuminate\Database\Query\JoinClause` instance which allows you to specify constraints on the `join` clause: DB::table('users') ->join('contacts', function ($join) { @@ -311,7 +313,7 @@ You may also specify more advanced join clauses. To get started, pass a closure }) ->get(); -If you would like to use a "where" style clause on your joins, you may use the `where` and `orWhere` methods on a join. Instead of comparing two columns, these methods will compare the column against a value: +If you would like to use a "where" clause on your joins, you may use the `where` and `orWhere` methods provided by the `JoinClause` instance. Instead of comparing two columns, these methods will compare the column against a value: DB::table('users') ->join('contacts', function ($join) { @@ -323,7 +325,7 @@ If you would like to use a "where" style clause on your joins, you may use the ` #### Subquery Joins -You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a query to a subquery. Each of these methods receive three arguments: the subquery, its table alias, and a closure that defines the related columns: +You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a query to a subquery. Each of these methods receive three arguments: the subquery, its table alias, and a closure that defines the related columns. In this example, we will retrieve a collection of users where each user record also contains the `created_at` timestamp of the user's most recently published blog post: $latestPosts = DB::table('posts') ->select('user_id', DB::raw('MAX(created_at) as last_post_created_at')) @@ -338,7 +340,9 @@ You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a q ## Unions -The query builder also provides a quick way to "union" two or more queries together. For example, you may create an initial query and use the `union` method to union it with more queries: +The query builder also provides a convenient method to "union" two or more queries together. For example, you may create an initial query and use the `union` method to union it with more queries: + + use Illuminate\Support\Facades\DB; $first = DB::table('users') ->whereNull('first_name'); @@ -348,7 +352,7 @@ The query builder also provides a quick way to "union" two or more queries toget ->union($first) ->get(); -> {tip} The `unionAll` method is also available and has the same method signature as `union`. +In addition to the `union` method, the query builder provides a `unionAll` method. Queries that are combined using the `unionAll` method will not have their duplicate results removed. The `unionAll` method has the same method signature as the `union` method. ## Where Clauses @@ -356,9 +360,9 @@ The query builder also provides a quick way to "union" two or more queries toget #### Simple Where Clauses -You may use the `where` method on a query builder instance to add `where` clauses to the query. The most basic call to `where` requires three arguments. The first argument is the name of the column. The second argument is an operator, which can be any of the database's supported operators. Finally, the third argument is the value to evaluate against the column. +You may use the query builder's `where` method to add "where" clauses to the query. The most basic call to the `where` method requires three arguments. The first argument is the name of the column. The second argument is an operator, which can be any of the database's supported operators. The third argument is the value to evaluate against the column. -For example, here is a query that verifies the value of the "votes" column is equal to 100: +For example, the following query retrieves users where the value of the "votes" column is equal to 100: $users = DB::table('users')->where('votes', '=', 100)->get(); From 2c723215fa7d9f962c65df6d639b52fd3323d172 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sat, 21 Nov 2020 13:23:34 -0600 Subject: [PATCH 153/274] wip --- queues.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/queues.md b/queues.md index 18f5debbeb..f37f698c4c 100644 --- a/queues.md +++ b/queues.md @@ -281,6 +281,22 @@ In certain cases, you may want to define a specific "key" that makes the job uni In the example above, the `UpdateSearchIndex` job is unique by a product ID. So, any new dispatches of the job with the same product ID will be ignored until the existing job has completed processing. In addition, if the existing job is not processed within one hour, the unique lock will be released and another job with the same unique key can be dispatched to the queue. + +#### Keeping Jobs Unique Until Processing Begins + +By default, unique jobs are "unlocked" after a job completes processing or fails all of its retry attempts. However, there may be situations where you would like your job to unlock immediately before it is processed. To accomplish this, your job should implement the `ShouldBeUniqueUntilProcessing` contract instead of the `ShouldBeUnique` contract: + + #### Unique Job Locks From 889cdd3935159e84bccd4a29bf6e29e5ab56efcd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 23 Nov 2020 15:57:10 -0600 Subject: [PATCH 154/274] wip --- queries.md | 87 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/queries.md b/queries.md index 95eaededf5..6c8a60a892 100644 --- a/queries.md +++ b/queries.md @@ -360,17 +360,20 @@ In addition to the `union` method, the query builder provides a `unionAll` metho #### Simple Where Clauses -You may use the query builder's `where` method to add "where" clauses to the query. The most basic call to the `where` method requires three arguments. The first argument is the name of the column. The second argument is an operator, which can be any of the database's supported operators. The third argument is the value to evaluate against the column. +You may use the query builder's `where` method to add "where" clauses to the query. The most basic call to the `where` method requires three arguments. The first argument is the name of the column. The second argument is an operator, which can be any of the database's supported operators. The third argument is the value to compare against the column's value. -For example, the following query retrieves users where the value of the "votes" column is equal to 100: +For example, the following query retrieves users where the value of the `votes` column is equal to `100` and the value of the `age` column is greater than `35`: - $users = DB::table('users')->where('votes', '=', 100)->get(); + $users = DB::table('users') + ->where('votes', '=', 100) + ->where('age', '>', 35) + ->get(); -For convenience, if you want to verify that a column is equal to a given value, you may pass the value directly as the second argument to the `where` method: +For convenience, if you want to verify that a column is `=` to a given value, you may pass the value as the second argument to the `where` method. Laravel will assume you would like to use the `=` operator: $users = DB::table('users')->where('votes', 100)->get(); -You may use a variety of other operators when writing a `where` clause: +As previously mentioned, you may use any operator that is supported by your database system: $users = DB::table('users') ->where('votes', '>=', 100) @@ -384,7 +387,7 @@ You may use a variety of other operators when writing a `where` clause: ->where('name', 'like', 'T%') ->get(); -You may also pass an array of conditions to the `where` function: +You may also pass an array of conditions to the `where` function. Each element of the array should be an array containing the three arguments typically passed to the `where` method: $users = DB::table('users')->where([ ['status', '=', '1'], @@ -394,7 +397,7 @@ You may also pass an array of conditions to the `where` function: #### Or Statements -You may chain where constraints together as well as add `or` clauses to the query. The `orWhere` method accepts the same arguments as the `where` method: +When chaining together calls to the query builder's `where` method, the "where" clauses will be joined together using the `and` operator. However, you may use the `orWhere` method to join a clause to the query using the `or` operator. The `orWhere` method accepts the same arguments as the `where` method: $users = DB::table('users') ->where('votes', '>', 100) @@ -411,7 +414,13 @@ If you need to group an "or" condition within parentheses, you may pass a closur }) ->get(); - // SQL: select * from users where votes > 100 or (name = 'Abigail' and votes > 50) +The example above will produce the following SQL: + +```sql +select * from users where votes > 100 or (name = 'Abigail' and votes > 50) +``` + +> {note} You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. #### Additional Where Clauses @@ -440,27 +449,27 @@ The `whereIn` method verifies that a given column's value is contained within th ->whereIn('id', [1, 2, 3]) ->get(); -The `whereNotIn` method verifies that the given column's value is **not** contained in the given array: +The `whereNotIn` method verifies that the given column's value is not contained in the given array: $users = DB::table('users') ->whereNotIn('id', [1, 2, 3]) ->get(); -> {note} If you are adding a huge array of integer bindings to your query, the `whereIntegerInRaw` or `whereIntegerNotInRaw` methods may be used to greatly reduce your memory usage. +> {note} If you are adding a large array of integer bindings to your query, the `whereIntegerInRaw` or `whereIntegerNotInRaw` methods may be used to greatly reduce your memory usage. **whereNull / whereNotNull / orWhereNull / orWhereNotNull** The `whereNull` method verifies that the value of the given column is `NULL`: $users = DB::table('users') - ->whereNull('updated_at') - ->get(); + ->whereNull('updated_at') + ->get(); The `whereNotNull` method verifies that the column's value is not `NULL`: $users = DB::table('users') - ->whereNotNull('updated_at') - ->get(); + ->whereNotNull('updated_at') + ->get(); **whereDate / whereMonth / whereDay / whereYear / whereTime** @@ -470,13 +479,13 @@ The `whereDate` method may be used to compare a column's value against a date: ->whereDate('created_at', '2016-12-31') ->get(); -The `whereMonth` method may be used to compare a column's value against a specific month of a year: +The `whereMonth` method may be used to compare a column's value against a specific month: $users = DB::table('users') ->whereMonth('created_at', '12') ->get(); -The `whereDay` method may be used to 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 the month: $users = DB::table('users') ->whereDay('created_at', '31') @@ -502,13 +511,13 @@ The `whereColumn` method may be used to verify that two columns are equal: ->whereColumn('first_name', 'last_name') ->get(); -You may also pass a comparison operator to the method: +You may also pass a comparison operator to the `whereColumn` method: $users = DB::table('users') ->whereColumn('updated_at', '>', 'created_at') ->get(); -The `whereColumn` method can also be passed an array of multiple conditions. These conditions will be joined using the `and` operator: +You may also pass an array of column comparisons to the `whereColumn` method. These conditions will be joined using the `and` operator: $users = DB::table('users') ->whereColumn([ @@ -519,7 +528,7 @@ The `whereColumn` method can also be passed an array of multiple conditions. The ### Parameter Grouping -Sometimes you may need to create more advanced where clauses such as "where exists" clauses or nested parameter groupings. The Laravel query builder can handle these as well. To get started, let's look at an example of grouping constraints within parenthesis: +Sometimes you may need to group several "where" clauses within parentheses in order to achieve your query's desired logical grouping. In fact, you should generally always group calls to the `orWhere` method in parentheses in order to avoid unexpected query behavior. To accomplish this, you may pass a closure to the `where` method: $users = DB::table('users') ->where('name', '=', 'John') @@ -531,71 +540,73 @@ Sometimes you may need to create more advanced where clauses such as "where exis As you can see, passing a closure into the `where` method instructs the query builder to begin a constraint group. The closure will receive a query builder instance which you can use to set the constraints that should be contained within the parenthesis group. The example above will produce the following SQL: - select * from users where name = 'John' and (votes > 100 or title = 'Admin') +```sql +select * from users where name = 'John' and (votes > 100 or title = 'Admin') +``` -> {tip} You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. +> {note} You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. ### Where Exists Clauses -The `whereExists` method allows you to write `where exists` SQL clauses. The `whereExists` method accepts a closure argument, which will receive a query builder instance allowing you to define the query that should be placed inside of the "exists" clause: +The `whereExists` method allows you to write "where exists" SQL clauses. The `whereExists` method accepts a closure which will receive a query builder instance, allowing you to define the query that should be placed inside of the "exists" clause: $users = DB::table('users') ->whereExists(function ($query) { $query->select(DB::raw(1)) ->from('orders') - ->whereRaw('orders.user_id = users.id'); + ->whereColumn('orders.user_id', 'users.id'); }) ->get(); The query above will produce the following SQL: - select * from users - where exists ( - select 1 from orders where orders.user_id = users.id - ) +```sql +select * from users +where exists ( + select 1 + from orders + where orders.user_id = users.id +) +``` ### Subquery Where Clauses -Sometimes you may need to construct a where clause that compares the results of a subquery to a given value. You may accomplish this by passing a closure and a value to the `where` method. For example, the following query will retrieve all users who have a recent "membership" of a given type; +Sometimes you may need to construct a "where" clause that compares the results of a subquery to a given value. You may accomplish this by passing a closure and a value to the `where` method. For example, the following query will retrieve all users who have a recent "membership" of a given type; use App\Models\User; $users = User::where(function ($query) { $query->select('type') ->from('membership') - ->whereColumn('user_id', 'users.id') - ->orderByDesc('start_date') + ->whereColumn('membership.user_id', 'users.id') + ->orderByDesc('membership.start_date') ->limit(1); }, 'Pro')->get(); ### JSON Where Clauses -Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MySQL 5.7, PostgreSQL, SQL Server 2016, and SQLite 3.9.0 (with the [JSON1 extension](https://www.sqlite.org/json1.html)). To query a JSON column, use the `->` operator: - - $users = DB::table('users') - ->where('options->language', 'en') - ->get(); +Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MySQL 5.7+, PostgreSQL, SQL Server 2016, and SQLite 3.9.0 (with the [JSON1 extension](https://www.sqlite.org/json1.html)). To query a JSON column, use the `->` operator: $users = DB::table('users') ->where('preferences->dining->meal', 'salad') ->get(); -You may use `whereJsonContains` to query JSON arrays (not supported on SQLite): +You may use `whereJsonContains` to query JSON arrays. This feature is not supported by the SQLite database: $users = DB::table('users') ->whereJsonContains('options->languages', 'en') ->get(); -MySQL and PostgreSQL support `whereJsonContains` with multiple values: +If your application uses the MySQL or PostgreSQL databases, you may pass an array of values to the `whereJsonContains` method: $users = DB::table('users') ->whereJsonContains('options->languages', ['en', 'de']) ->get(); -You may use `whereJsonLength` to query JSON arrays by their length: +You may use `whereJsonLength` method to query JSON arrays by their length: $users = DB::table('users') ->whereJsonLength('options->languages', 0) From 63e8cd1cfd51c0deded9459e663d4111edb94f59 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 23 Nov 2020 16:11:01 -0600 Subject: [PATCH 155/274] wip --- queries.md | 93 +++++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/queries.md b/queries.md index 6c8a60a892..27bf53991d 100644 --- a/queries.md +++ b/queries.md @@ -8,11 +8,15 @@ - [Raw Expressions](#raw-expressions) - [Joins](#joins) - [Unions](#unions) -- [Where Clauses](#where-clauses) - - [Parameter Grouping](#parameter-grouping) +- [Basic Where Clauses](#basic-where-clauses) + - [Where Clauses](#where-clauses) + - [Or Where Clauses](#or-where-clauses) + - [JSON Where Clauses](#json-where-clauses) + - [Additional Where Clauses](#additional-where-clauses) + - [Logical Grouping](#logical-grouping) +- [Advanced Where Clauses](#advanced-where-clauses) - [Where Exists Clauses](#where-exists-clauses) - [Subquery Where Clauses](#subquery-where-clauses) - - [JSON Where Clauses](#json-where-clauses) - [Ordering, Grouping, Limit & Offset](#ordering-grouping-limit-and-offset) - [Conditional Clauses](#conditional-clauses) - [Inserts](#inserts) @@ -354,11 +358,11 @@ The query builder also provides a convenient method to "union" two or more queri In addition to the `union` method, the query builder provides a `unionAll` method. Queries that are combined using the `unionAll` method will not have their duplicate results removed. The `unionAll` method has the same method signature as the `union` method. - -## Where Clauses + +## Basic Where Clauses - -#### Simple Where Clauses + +### Where Clauses You may use the query builder's `where` method to add "where" clauses to the query. The most basic call to the `where` method requires three arguments. The first argument is the name of the column. The second argument is an operator, which can be any of the database's supported operators. The third argument is the value to compare against the column's value. @@ -394,8 +398,8 @@ You may also pass an array of conditions to the `where` function. Each element o ['subscribed', '<>', '1'], ])->get(); - -#### Or Statements + +### Or Where Clauses When chaining together calls to the query builder's `where` method, the "where" clauses will be joined together using the `and` operator. However, you may use the `orWhere` method to join a clause to the query using the `or` operator. The `orWhere` method accepts the same arguments as the `where` method: @@ -422,8 +426,39 @@ select * from users where votes > 100 or (name = 'Abigail' and votes > 50) > {note} You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. + +### JSON Where Clauses + +Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MySQL 5.7+, PostgreSQL, SQL Server 2016, and SQLite 3.9.0 (with the [JSON1 extension](https://www.sqlite.org/json1.html)). To query a JSON column, use the `->` operator: + + $users = DB::table('users') + ->where('preferences->dining->meal', 'salad') + ->get(); + +You may use `whereJsonContains` to query JSON arrays. This feature is not supported by the SQLite database: + + $users = DB::table('users') + ->whereJsonContains('options->languages', 'en') + ->get(); + +If your application uses the MySQL or PostgreSQL databases, you may pass an array of values to the `whereJsonContains` method: + + $users = DB::table('users') + ->whereJsonContains('options->languages', ['en', 'de']) + ->get(); + +You may use `whereJsonLength` method to query JSON arrays by their length: + + $users = DB::table('users') + ->whereJsonLength('options->languages', 0) + ->get(); + + $users = DB::table('users') + ->whereJsonLength('options->languages', '>', 1) + ->get(); + -#### Additional Where Clauses +### Additional Where Clauses **whereBetween / orWhereBetween** @@ -525,8 +560,8 @@ You may also pass an array of column comparisons to the `whereColumn` method. Th ['updated_at', '>', 'created_at'], ])->get(); - -### Parameter Grouping + +### Logical Grouping Sometimes you may need to group several "where" clauses within parentheses in order to achieve your query's desired logical grouping. In fact, you should generally always group calls to the `orWhere` method in parentheses in order to avoid unexpected query behavior. To accomplish this, you may pass a closure to the `where` method: @@ -546,6 +581,9 @@ select * from users where name = 'John' and (votes > 100 or title = 'Admin') > {note} You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. + +### Advanced Where Clauses + ### Where Exists Clauses @@ -585,37 +623,6 @@ Sometimes you may need to construct a "where" clause that compares the results o ->limit(1); }, 'Pro')->get(); - -### JSON Where Clauses - -Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MySQL 5.7+, PostgreSQL, SQL Server 2016, and SQLite 3.9.0 (with the [JSON1 extension](https://www.sqlite.org/json1.html)). To query a JSON column, use the `->` operator: - - $users = DB::table('users') - ->where('preferences->dining->meal', 'salad') - ->get(); - -You may use `whereJsonContains` to query JSON arrays. This feature is not supported by the SQLite database: - - $users = DB::table('users') - ->whereJsonContains('options->languages', 'en') - ->get(); - -If your application uses the MySQL or PostgreSQL databases, you may pass an array of values to the `whereJsonContains` method: - - $users = DB::table('users') - ->whereJsonContains('options->languages', ['en', 'de']) - ->get(); - -You may use `whereJsonLength` method to query JSON arrays by their length: - - $users = DB::table('users') - ->whereJsonLength('options->languages', 0) - ->get(); - - $users = DB::table('users') - ->whereJsonLength('options->languages', '>', 1) - ->get(); - ## Ordering, Grouping, Limit & Offset From 565e83c83f8bb281ecbeb70f76e363d0834b561d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 23 Nov 2020 16:41:33 -0600 Subject: [PATCH 156/274] wip --- queries.md | 101 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/queries.md b/queries.md index 27bf53991d..f4f73ce225 100644 --- a/queries.md +++ b/queries.md @@ -18,8 +18,12 @@ - [Where Exists Clauses](#where-exists-clauses) - [Subquery Where Clauses](#subquery-where-clauses) - [Ordering, Grouping, Limit & Offset](#ordering-grouping-limit-and-offset) + - [Ordering](#ordering) + - [Grouping](#grouping) + - [Limit & Offset](#limit-and-offset) - [Conditional Clauses](#conditional-clauses) - [Inserts](#inserts) + - [Upserts](#upserts) - [Updates](#updates) - [Updating JSON Columns](#updating-json-columns) - [Increment & Decrement](#increment-and-decrement) @@ -626,16 +630,19 @@ Sometimes you may need to construct a "where" clause that compares the results o ## Ordering, Grouping, Limit & Offset + +### Ordering + -#### orderBy +#### The `orderBy` Method -The `orderBy` method allows you to sort the result of the query by a given column. The first argument to the `orderBy` method should be the column you wish to sort by, while the second argument controls the direction of the sort and may be either `asc` or `desc`: +The `orderBy` method allows you to sort the results of the query by a given column. The first argument accepted by the `orderBy` method should be the column you wish to sort by, while the second argument determines the direction of the sort and may be either `asc` or `desc`: $users = DB::table('users') ->orderBy('name', 'desc') ->get(); -If you need to sort by multiple columns, you may invoke `orderBy` as many times as needed: +To sort by multiple columns, you may simply invoke `orderBy` as many times as necessary: $users = DB::table('users') ->orderBy('name', 'desc') @@ -643,16 +650,16 @@ If you need to sort by multiple columns, you may invoke `orderBy` as many times ->get(); -#### latest / oldest +#### The `latest` & `oldest` Methods -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: +The `latest` and `oldest` methods allow you to easily order results by date. By default, result will be ordered by the table's `created_at` column. Or, you may pass the column name that you wish to sort by: $user = DB::table('users') ->latest() ->first(); - -#### inRandomOrder + +#### Random Ordering The `inRandomOrder` method may be used to sort the query results randomly. For example, you may use this method to fetch a random user: @@ -660,25 +667,28 @@ The `inRandomOrder` method may be used to sort the query results randomly. For e ->inRandomOrder() ->first(); - -#### reorder + +#### Removing Existing Orderings -The `reorder` method allows you to remove all the existing orders and optionally apply a new order. For example, you can remove all the existing orders: +The `reorder` method removes all of the "order by" clauses that have previously been applied to the query: $query = DB::table('users')->orderBy('name'); $unorderedUsers = $query->reorder()->get(); -To remove all existing orders and apply a new order, provide the column and direction as arguments to the method: +You may pass a column and direction when calling the `reorder` method in order to remove all existing "order by "clauses" and apply an entirely new order to the query: $query = DB::table('users')->orderBy('name'); $usersOrderedByEmail = $query->reorder('email', 'desc')->get(); + +### Grouping + -#### groupBy / having +#### The `groupBy` & `having` Methods -The `groupBy` and `having` methods may be used to group the query results. The `having` method's signature is similar to that of the `where` method: +As you might expect, the `groupBy` and `having` methods may be used to group the query results. The `having` method's signature is similar to that of the `where` method: $users = DB::table('users') ->groupBy('account_id') @@ -692,16 +702,19 @@ You may pass multiple arguments to the `groupBy` method to group by multiple col ->having('account_id', '>', 100) ->get(); -For more advanced `having` statements, see the [`havingRaw`](#raw-methods) method. +To build more advanced `having` statements, see the [`havingRaw`](#raw-methods) method. + + +### Limit & Offset -#### skip / take +#### The `skip` & `take` Methods -To limit the number of results returned from the query, or to skip a given number of results in the query, you may use the `skip` and `take` methods: +You may use the `skip` and `take` methods to limit the number of results returned from the query or to skip a given number of results in the query: $users = DB::table('users')->skip(10)->take(5)->get(); -Alternatively, you may use the `limit` and `offset` methods: +Alternatively, you may use the `limit` and `offset` methods. These methods are functionally equivalent to the `take` and `skip` methods, respectively: $users = DB::table('users') ->offset(10) @@ -711,7 +724,7 @@ Alternatively, you may use the `limit` and `offset` methods: ## Conditional Clauses -Sometimes you may want clauses to apply to a query only when something else is true. For instance you may only want to apply a `where` statement if a given input value is present on the incoming request. You may accomplish this using the `when` method: +Sometimes you may want certain query clauses to apply to a query based on another condition. For instance, you may only want to apply a `where` statement if a given input value is present on the incoming HTTP request. You may accomplish this using the `when` method: $role = $request->input('role'); @@ -721,15 +734,15 @@ Sometimes you may want clauses to apply to a query only when something else is t }) ->get(); -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. +The `when` method only executes the given closure when the first argument is `true`. If the first argument is `false`, the closure will not be executed. So, in the example above, the closure given to the `when` method will only be invoked if the `role` field is present on the incoming request and evaluates to `true`. -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: +You may pass another closure as the third argument to the `when` method. This closure will only execute if the first argument evaluates as `false`. To illustrate how this feature may be used, we will use it to configure the default ordering of a query: - $sortBy = null; + $sortByVotes = $request->input('sort_by_votes'); $users = DB::table('users') - ->when($sortBy, function ($query, $sortBy) { - return $query->orderBy($sortBy); + ->when($sortByVotes, function ($query, $sortByVotes) { + return $query->orderBy('votes'); }, function ($query) { return $query->orderBy('name'); }) @@ -738,35 +751,27 @@ You may pass another closure as the third parameter to the `when` method. This c ## Inserts -The query builder also provides an `insert` method for inserting records into the database table. The `insert` method accepts an array of column names and values: +The query builder also provides an `insert` method that may be used to insert records into the database table. The `insert` method accepts an array of column names and values: - DB::table('users')->insert( - ['email' => 'john@example.com', 'votes' => 0] - ); + DB::table('users')->insert([ + 'email' => 'kayla@example.com', + 'votes' => 0 + ]); -You may even insert several records into the table with a single call to `insert` by passing an array of arrays. Each array represents a row to be inserted into the table: +You may insert several records at once by passing an array of arrays. Each array represents a record that should be inserted into the table: DB::table('users')->insert([ - ['email' => 'taylor@example.com', 'votes' => 0], - ['email' => 'dayle@example.com', 'votes' => 0], + ['email' => 'picard@example.com', 'votes' => 0], + ['email' => 'janeway@example.com', 'votes' => 0], ]); The `insertOrIgnore` method will ignore duplicate record errors while inserting records into the database: DB::table('users')->insertOrIgnore([ - ['id' => 1, 'email' => 'taylor@example.com'], - ['id' => 2, 'email' => 'dayle@example.com'], + ['id' => 1, 'email' => 'sisko@example.com'], + ['id' => 2, 'email' => 'archer@example.com'], ]); -The `upsert` method will insert rows that do not exist and update the rows that already exist with the new values. The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table. The method's third and final argument is an array of columns that should be updated if a matching record already exists in the database: - - DB::table('flights')->upsert([ - ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], - ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] - ], ['departure', 'destination'], ['price']); - -> {note} All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. - #### Auto-Incrementing IDs @@ -778,6 +783,20 @@ If the table has an auto-incrementing id, use the `insertGetId` method to insert > {note} When using PostgreSQL the `insertGetId` method expects the auto-incrementing column to be named `id`. If you would like to retrieve the ID from a different "sequence", you may pass the column name as the second parameter to the `insertGetId` method. + +### Upserts + +The `upsert` method will insert records that do not exist and update the records that already exist with new values that you may specify. The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table. The method's third and final argument is an array of columns that should be updated if a matching record already exists in the database: + + DB::table('flights')->upsert([ + ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], + ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] + ], ['departure', 'destination'], ['price']); + +In the example above, Laravel will attempt to insert two records. If a record already exists with the same `departure` and `destination` column values, Laravel will update that record's `price` column. + +> {note} All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. + ## Updates From 10f930152b8bff864e575576f152ecd62afb0569 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 23 Nov 2020 16:53:01 -0600 Subject: [PATCH 157/274] wip --- queries.md | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/queries.md b/queries.md index f4f73ce225..dc41a0cb61 100644 --- a/queries.md +++ b/queries.md @@ -4,7 +4,7 @@ - [Running Database Queries](#running-database-queries) - [Chunking Results](#chunking-results) - [Aggregates](#aggregates) -- [Selects](#selects) +- [Select Statements](#selects) - [Raw Expressions](#raw-expressions) - [Joins](#joins) - [Unions](#unions) @@ -22,12 +22,12 @@ - [Grouping](#grouping) - [Limit & Offset](#limit-and-offset) - [Conditional Clauses](#conditional-clauses) -- [Inserts](#inserts) +- [Insert Statements](#inserts) - [Upserts](#upserts) -- [Updates](#updates) +- [Update Statements](#updates) - [Updating JSON Columns](#updating-json-columns) - [Increment & Decrement](#increment-and-decrement) -- [Deletes](#deletes) +- [Delete Statements](#deletes) - [Pessimistic Locking](#pessimistic-locking) - [Debugging](#debugging) @@ -185,7 +185,7 @@ Instead of using the `count` method to determine if any records exist that match } -## Selects +## Select Statements #### Specifying A Select Clause @@ -749,7 +749,7 @@ You may pass another closure as the third argument to the `when` method. This cl ->get(); -## Inserts +## Insert Statements The query builder also provides an `insert` method that may be used to insert records into the database table. The `insert` method accepts an array of column names and values: @@ -798,9 +798,9 @@ In the example above, Laravel will attempt to insert two records. If a record al > {note} All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. -## Updates +## Update Statements -In addition to inserting records into the database, the query builder can also update existing records using the `update` method. The `update` method, like the `insert` method, accepts an array of column and value pairs containing the columns to be updated. You may constrain the `update` query using `where` clauses: +In addition to inserting records into the database, the query builder can also update existing records using the `update` method. The `update` method, like the `insert` method, accepts an array of column and value pairs indicating the columns to be updated. You may constrain the `update` query using `where` clauses: $affected = DB::table('users') ->where('id', 1) @@ -809,9 +809,9 @@ In addition to inserting records into the database, the query builder can also u #### Update Or Insert -Sometimes you may want to update an existing record in the database or create it if no matching record exists. In this scenario, the `updateOrInsert` method may be used. The `updateOrInsert` method accepts two arguments: an array of conditions by which to find the record, and an array of column and value pairs containing the columns to be updated. +Sometimes you may want to update an existing record in the database or create it if no matching record exists. In this scenario, the `updateOrInsert` method may be used. The `updateOrInsert` method accepts two arguments: an array of conditions by which to find the record, and an array of column and value pairs indicating the columns to be updated. -The `updateOrInsert` method will first attempt to locate a matching database record using the first argument's column and value pairs. If the record exists, it will be updated with the values in the second argument. If the record can not be found, a new record will be inserted with the merged attributes of both arguments: +The `updateOrInsert` method will attempt to locate a matching database record using the first argument's column and value pairs. If the record exists, it will be updated with the values in the second argument. If the record can not be found, a new record will be inserted with the merged attributes of both arguments: DB::table('users') ->updateOrInsert( @@ -822,7 +822,7 @@ The `updateOrInsert` method will first attempt to locate a matching database rec ### Updating JSON Columns -When updating a JSON column, you should use `->` syntax to access the appropriate key in the JSON object. This operation is supported on MySQL 5.7+ and PostgreSQL 9.5+: +When updating a JSON column, you should use `->` syntax to update the appropriate key in the JSON object. This operation is supported on MySQL 5.7+ and PostgreSQL 9.5+: $affected = DB::table('users') ->where('id', 1) @@ -831,9 +831,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 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: +The query builder also provides convenient methods for incrementing or decrementing the value of a given column. Both of these methods accept at least one argument: the column to modify. A second argument may be provided to specify the amount by which the column should be incremented or decremented: DB::table('users')->increment('votes'); @@ -847,18 +845,16 @@ You may also specify additional columns to update during the operation: DB::table('users')->increment('votes', 1, ['name' => 'John']); -> {note} Model events are not fired when using the `increment` and `decrement` methods. - -## Deletes +## Delete Statements -The query builder may also be used to delete records from the table via the `delete` method. You may constrain `delete` statements by adding `where` clauses before calling the `delete` method: +The query builder's `delete` method may be used to delete records from the table. You may constrain `delete` statements by adding "where" clauses before calling the `delete` method: DB::table('users')->delete(); DB::table('users')->where('votes', '>', 100)->delete(); -If you wish to truncate the entire table, which will remove all rows and reset the auto-incrementing ID to zero, you may use the `truncate` method: +If you wish to truncate an entire table, which will remove all records from the table and reset the auto-incrementing ID to zero, you may use the `truncate` method: DB::table('users')->truncate(); @@ -870,18 +866,24 @@ When truncating a PostgreSQL database, the `CASCADE` behavior will be applied. T ## Pessimistic Locking -The query builder also includes a few functions to help you do "pessimistic locking" on your `select` statements. To run the statement with a "shared lock", you may use the `sharedLock` method on a query. A shared lock prevents the selected rows from being modified until your transaction commits: +The query builder also includes a few functions to help you achieve "pessimistic locking" when executing your `select` statements. To execute a statement with a "shared lock", you may call the `sharedLock` method. A shared lock prevents the selected rows from being modified until your transaction is committed: - DB::table('users')->where('votes', '>', 100)->sharedLock()->get(); + DB::table('users') + ->where('votes', '>', 100) + ->sharedLock() + ->get(); -Alternatively, you may use the `lockForUpdate` method. A "for update" lock prevents the rows from being modified or from being selected with another shared lock: +Alternatively, you may use the `lockForUpdate` method. A "for update" lock prevents the selected records from being modified or from being selected with another shared lock: - DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get(); + DB::table('users') + ->where('votes', '>', 100) + ->lockForUpdate() + ->get(); ## Debugging -You may use the `dd` or `dump` methods while building a query to dump the query bindings and SQL. The `dd` method will display the debug information and then stop executing the request. The `dump` method will display the debug information but allow the request to keep executing: +You may use the `dd` and `dump` methods while building a query to dump the current query bindings and SQL. The `dd` method will display the debug information and then stop executing the request. The `dump` method will display the debug information but allow the request to continue executing: DB::table('users')->where('votes', '>', 100)->dd(); From 1bcad00dc5eaf9a8eb6fbc4001c664e6e7c873ff Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 23 Nov 2020 16:55:04 -0600 Subject: [PATCH 158/274] wip --- queries.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/queries.md b/queries.md index dc41a0cb61..bca15d0eae 100644 --- a/queries.md +++ b/queries.md @@ -190,11 +190,13 @@ Instead of using the `count` method to determine if any records exist that match #### Specifying A Select Clause -You may not always want to select all columns from a database table. Using the `select` method, you can specify a custom `select` clause for the query: +You may not always want to select all columns from a database table. Using the `select` method, you can specify a custom "select" clause for the query: use Illuminate\Support\Facades\DB; - $users = DB::table('users')->select('name', 'email as user_email')->get(); + $users = DB::table('users') + ->select('name', 'email as user_email') + ->get(); The `distinct` method allows you to force the query to return distinct results: @@ -236,7 +238,7 @@ The `selectRaw` method can be used in place of `addSelect(DB::raw(...))`. This m #### `whereRaw / orWhereRaw` -The `whereRaw` and `orWhereRaw` methods can be used to inject a raw `where` clause into your query. These methods accept an optional array of bindings as their second argument: +The `whereRaw` and `orWhereRaw` methods can be used to inject a raw "where" clause into your query. These methods accept an optional array of bindings as their second argument: $orders = DB::table('orders') ->whereRaw('price > IF(state = "TX", ?, 100)', [200]) @@ -245,7 +247,7 @@ The `whereRaw` and `orWhereRaw` methods can be used to inject a raw `where` clau #### `havingRaw / orHavingRaw` -The `havingRaw` and `orHavingRaw` methods may be used to provide a raw string as the value of the `having` clause. These methods accept an optional array of bindings as their second argument: +The `havingRaw` and `orHavingRaw` methods may be used to provide a raw string as the value of the "having" clause. These methods accept an optional array of bindings as their second argument: $orders = DB::table('orders') ->select('department', DB::raw('SUM(price) as total_sales')) @@ -256,7 +258,7 @@ The `havingRaw` and `orHavingRaw` methods may be used to provide a raw string as #### `orderByRaw` -The `orderByRaw` method may be used to provide a raw string as the value of the `order by` clause: +The `orderByRaw` method may be used to provide a raw string as the value of the "order by" clause: $orders = DB::table('orders') ->orderByRaw('updated_at - created_at DESC') @@ -313,7 +315,7 @@ You may use the `crossJoin` method to perform a "cross join". Cross joins genera #### Advanced Join Clauses -You may also specify more advanced join clauses. To get started, pass a closure as the second argument to the `join` method. The closure will receive a `Illuminate\Database\Query\JoinClause` instance which allows you to specify constraints on the `join` clause: +You may also specify more advanced join clauses. To get started, pass a closure as the second argument to the `join` method. The closure will receive a `Illuminate\Database\Query\JoinClause` instance which allows you to specify constraints on the "join" clause: DB::table('users') ->join('contacts', function ($join) { From 02243d9054ca413ee8039a9274e710439a36e61b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 09:24:10 -0600 Subject: [PATCH 159/274] document sorting by multiple columns --- collections.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/collections.md b/collections.md index 94fd2dc678..2d2ac4566d 100644 --- a/collections.md +++ b/collections.md @@ -1999,6 +1999,56 @@ Alternatively, you may pass your own closure to determine how to sort the collec ] */ +If you would like to sort your collection by multiple attributes, you may pass an array of sort operations to the `sortBy` method. Each sort operation should be an array consisting of the attribute that you wish to sort by and the direction of the desired sort: + + $collection = collect([ + ['name' => 'Taylor Otwell', 'age' => 34], + ['name' => 'Abigail Otwell', 'age' => 30], + ['name' => 'Taylor Otwell', 'age' => 36], + ['name' => 'Abigail Otwell', 'age' => 32], + ]); + + $sorted = $collection->sortBy([ + ['name', 'asc'], + ['age', 'desc'], + ]); + + $sorted->values()->all(); + + /* + [ + ['name' => 'Abigail Otwell', 'age' => 32], + ['name' => 'Abigail Otwell', 'age' => 30], + ['name' => 'Taylor Otwell', 'age' => 36], + ['name' => 'Taylor Otwell', 'age' => 34], + ] + */ + +When sorting a collection by multiple attributes, you may also provide closures that define each sort operation: + + $collection = collect([ + ['name' => 'Taylor Otwell', 'age' => 34], + ['name' => 'Abigail Otwell', 'age' => 30], + ['name' => 'Taylor Otwell', 'age' => 36], + ['name' => 'Abigail Otwell', 'age' => 32], + ]); + + $sorted = $collection->sortBy([ + fn ($a, $b) => $a['name'] <=> $b['name'], + fn ($a, $b) => $b['age'] <=> $a['age'], + ]); + + $sorted->values()->all(); + + /* + [ + ['name' => 'Abigail Otwell', 'age' => 32], + ['name' => 'Abigail Otwell', 'age' => 30], + ['name' => 'Taylor Otwell', 'age' => 36], + ['name' => 'Taylor Otwell', 'age' => 34], + ] + */ + #### `sortByDesc()` {#collection-method} From e565b19e10990a3ed6c8691125099c09265dc7ab Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 10:33:40 -0600 Subject: [PATCH 160/274] show other syntax --- queues.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/queues.md b/queues.md index f37f698c4c..36dacf6187 100644 --- a/queues.md +++ b/queues.md @@ -700,6 +700,33 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e } } +Alternatively, you may specify the job's queue by calling the `onQueue` method within the job's constructor: + + onQueue('processing'); + } + } + #### Dispatching To A Particular Connection @@ -738,6 +765,33 @@ You may chain the `onConnection` and `onQueue` methods together to specify the c ->onConnection('sqs') ->onQueue('processing'); +Alternatively, you may specify the job's connection by calling the `onConnection` method within the job's constructor: + + onConnection('sqs'); + } + } + ### Specifying Max Job Attempts / Timeout Values From f1ec287e2cca9f8e5cdea97b0ed868173738b970 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 10:53:04 -0600 Subject: [PATCH 161/274] update links --- queries.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/queries.md b/queries.md index bca15d0eae..352eb43a25 100644 --- a/queries.md +++ b/queries.md @@ -184,7 +184,7 @@ Instead of using the `count` method to determine if any records exist that match // ... } - + ## Select Statements @@ -750,7 +750,7 @@ You may pass another closure as the third argument to the `when` method. This cl }) ->get(); - + ## Insert Statements The query builder also provides an `insert` method that may be used to insert records into the database table. The `insert` method accepts an array of column names and values: @@ -799,7 +799,7 @@ In the example above, Laravel will attempt to insert two records. If a record al > {note} All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. - + ## Update Statements In addition to inserting records into the database, the query builder can also update existing records using the `update` method. The `update` method, like the `insert` method, accepts an array of column and value pairs indicating the columns to be updated. You may constrain the `update` query using `where` clauses: @@ -847,7 +847,7 @@ You may also specify additional columns to update during the operation: DB::table('users')->increment('votes', 1, ['name' => 'John']); - + ## Delete Statements The query builder's `delete` method may be used to delete records from the table. You may constrain `delete` statements by adding "where" clauses before calling the `delete` method: From 353b4a4893b8972615a2292b6bcb7346d33bfa5f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 10:53:32 -0600 Subject: [PATCH 162/274] update links --- queries.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/queries.md b/queries.md index 352eb43a25..626dcf9174 100644 --- a/queries.md +++ b/queries.md @@ -4,7 +4,7 @@ - [Running Database Queries](#running-database-queries) - [Chunking Results](#chunking-results) - [Aggregates](#aggregates) -- [Select Statements](#selects) +- [Select Statements](#select-statements) - [Raw Expressions](#raw-expressions) - [Joins](#joins) - [Unions](#unions) @@ -22,12 +22,12 @@ - [Grouping](#grouping) - [Limit & Offset](#limit-and-offset) - [Conditional Clauses](#conditional-clauses) -- [Insert Statements](#inserts) +- [Insert Statements](#insert-statements) - [Upserts](#upserts) -- [Update Statements](#updates) +- [Update Statements](#update-statements) - [Updating JSON Columns](#updating-json-columns) - [Increment & Decrement](#increment-and-decrement) -- [Delete Statements](#deletes) +- [Delete Statements](#delete-statements) - [Pessimistic Locking](#pessimistic-locking) - [Debugging](#debugging) From 38d172206d2d7fb064dbdc008edf0fc273a10afd Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 11:17:17 -0600 Subject: [PATCH 163/274] wip --- pagination.md | 111 +++++++++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/pagination.md b/pagination.md index 594304d3fc..fb3be7f062 100644 --- a/pagination.md +++ b/pagination.md @@ -14,7 +14,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. By default, the HTML generated by the paginator is compatible with the [Tailwind CSS framework](https://tailwindcss.com/); however, Bootstrap views are also available. +In other frameworks, pagination can be very painful. We hope Laravel's approach to pagination will be a breath of fresh air. 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 records with zero configuration. By default, the HTML generated by the paginator is compatible with the [Tailwind CSS framework](https://tailwindcss.com/); however, Bootstrap pagination support is also available. ## Basic Usage @@ -22,7 +22,7 @@ In other frameworks, pagination can be very painful. Laravel's paginator is inte ### Paginating Query Builder Results -There are several ways to paginate items. The simplest is by using the `paginate` method on the [query builder](/docs/{{version}}/queries) or an [Eloquent query](/docs/{{version}}/eloquent). The `paginate` method automatically takes care of setting the proper limit and offset based on the current page being viewed by the user. By default, the current page is detected by the value of the `page` query string argument on the HTTP request. This value is automatically detected by Laravel, and is also automatically inserted into links generated by the paginator. +There are several ways to paginate items. The simplest is by using the `paginate` method on the [query builder](/docs/{{version}}/queries) or an [Eloquent query](/docs/{{version}}/eloquent). The `paginate` method automatically takes care of setting the query's "limit" and "offset" based on the current page being viewed by the user. By default, the current page is detected by the value of the `page` query string argument on the HTTP request. This value is automatically detected by Laravel, and is also automatically inserted into links generated by the paginator. In this example, the only argument passed to the `paginate` method is the number of items you would like displayed "per page". In this case, let's specify that we would like to display `15` items per page: @@ -38,33 +38,35 @@ In this example, the only argument passed to the `paginate` method is the number /** * Show all of the users for the application. * - * @return Response + * @return \Illuminate\Http\Response */ public function index() { - $users = DB::table('users')->paginate(15); - - return view('user.index', ['users' => $users]); + return view('user.index', [ + 'users' => DB::table('users')->paginate(15) + ]); } } -> {note} Currently, pagination operations that use a `groupBy` statement cannot be executed efficiently by Laravel. If you need to use a `groupBy` with a paginated result set, it is recommended that you query the database and create a paginator manually. - #### "Simple Pagination" -If you only need to display simple "Next" and "Previous" links in your pagination view, you may use the `simplePaginate` method to perform a more efficient query. This is very useful for large datasets when you do not need to display a link for each page number when rendering your view: +The `paginate` method counts the total number of records matched by the query before retrieving the records from the database. This is done so that the paginator knows how many pages of records there are in total. However, if you do not plan to show the total number of pages in your application's UI then the record count query is unnecessary. + +Therefore, if you only need to display simple "Next" and "Previous" links in your application's UI, you may use the `simplePaginate` method to perform a single, efficient query: $users = DB::table('users')->simplePaginate(15); ### Paginating Eloquent Results -You may also paginate [Eloquent](/docs/{{version}}/eloquent) queries. In this example, we will paginate the `User` model with `15` items per page. As you can see, the syntax is nearly identical to paginating query builder results: +You may also paginate [Eloquent](/docs/{{version}}/eloquent) queries. In this example, we will paginate the `App\Models\User` model and indicate that we plan to display 15 records per page. As you can see, the syntax is nearly identical to paginating query builder results: + + use App\Models\User; - $users = App\Models\User::paginate(15); + $users = User::paginate(15); -You may call `paginate` after setting other constraints on the query, such as `where` clauses: +Of course, you may call the `paginate` method after setting other constraints on the query, such as `where` clauses: $users = User::where('votes', '>', 100)->paginate(15); @@ -75,11 +77,11 @@ You may also use the `simplePaginate` method when paginating Eloquent models: ### Manually Creating A Paginator -Sometimes you may wish to create a pagination instance manually, passing it an array of items. You may do so by creating either an `Illuminate\Pagination\Paginator` or `Illuminate\Pagination\LengthAwarePaginator` instance, depending on your needs. +Sometimes you may wish to create a pagination instance manually, passing it an array of items that you already have in memory. You may do so by creating either an `Illuminate\Pagination\Paginator` or `Illuminate\Pagination\LengthAwarePaginator` instance, depending on your needs. -The `Paginator` class does not need to know the total number of items in the result set; however, because of this, the class does not have methods for retrieving the index of the last page. The `LengthAwarePaginator` accepts almost the same arguments as the `Paginator`; however, it does require a count of the total number of items in the result set. +The `Paginator` class does not need to know the total number of items in the result set; however, because of this, the class does not have methods for retrieving the index of the last page. The `LengthAwarePaginator` accepts almost the same arguments as the `Paginator`; however, it requires a count of the total number of items in the result set. -In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder and Eloquent, while the `LengthAwarePaginator` corresponds to the `paginate` method. +In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder, 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](https://secure.php.net/manual/en/function.array-slice.php) PHP function. @@ -88,25 +90,29 @@ In other words, the `Paginator` corresponds to the `simplePaginate` method on th When calling the `paginate` method, you will receive an instance of `Illuminate\Pagination\LengthAwarePaginator`. When calling the `simplePaginate` method, you will receive an instance of `Illuminate\Pagination\Paginator`. These objects provide several methods that describe the result set. In addition to these helpers methods, the paginator instances are iterators and may be looped as an array. So, once you have retrieved the results, you may display the results and render the page links using [Blade](/docs/{{version}}/blade): -
- @foreach ($users as $user) - {{ $user->name }} - @endforeach -
+```html +
+ @foreach ($users as $user) + {{ $user->name }} + @endforeach +
- {{ $users->links() }} +{{ $users->links() }} +``` The `links` method will render the links to the rest of the pages in the result set. Each of these links will already contain the proper `page` query string variable. Remember, the HTML generated by the `links` method is compatible with the [Tailwind CSS framework](https://tailwindcss.com). #### Customizing The Paginator URI -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: +The paginator's `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/admin/users?page=N`, you should pass `/admin/users` to the `withPath` method: - Route::get('users', function () { - $users = App\Models\User::paginate(15); + use App\Models\User; - $users->withPath('custom/url'); + Route::get('/users', function () { + $users = User::paginate(15); + + $users->withPath('/admin/users'); // }); @@ -118,13 +124,13 @@ You may append to the query string of pagination links using the `appends` metho {{ $users->appends(['sort' => 'votes'])->links() }} -If you wish to append all current query string values to the pagination links you may use the `withQueryString` method: +You may use the `withQueryString` method if you would like to append all of the current request's query string values to the pagination links: {{ $users->withQueryString()->links() }} -If you wish to append a "hash fragment" to the paginator's URLs, you may use the `fragment` method. For example, to append `#foo` to the end of each pagination link, make the following call to the `fragment` method: +If you wish to append a "hash fragment" to the paginator's URLs, you may use the `fragment` method. For example, to append `#users` to the end of each pagination link, you may invoke the `fragment` method like so: - {{ $users->fragment('foo')->links() }} + {{ $users->fragment('users')->links() }} #### Adjusting The Pagination Link Window @@ -136,13 +142,15 @@ You may control how many additional links are displayed on each side of the pagi ### Converting Results To JSON -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 returning it from a route or controller action: +The Laravel paginator 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 returning it from a route or controller action: + + use App\Models\User; - Route::get('users', function () { - return App\Models\User::paginate(); + Route::get('/users', function () { + return User::paginate(); }); -The JSON from the paginator will include meta information such as `total`, `current_page`, `last_page`, and more. The actual result objects will be available via the `data` key in the JSON array. Here is an example of the JSON created by returning a paginator instance from a route: +The JSON from the paginator will include meta information such as `total`, `current_page`, `last_page`, and more. The result records are available via the `data` key in the JSON array. Here is an example of the JSON created by returning a paginator instance from a route: { "total": 50, @@ -158,10 +166,10 @@ The JSON from the paginator will include meta information such as `total`, `curr "to": 15, "data":[ { - // Result Object + // Record... }, { - // Result Object + // Record... } ] } @@ -169,37 +177,56 @@ The JSON from the paginator will include meta information such as `total`, `curr ## Customizing The Pagination View -By default, the views rendered to display the pagination links are compatible with the Tailwind CSS framework. However, if you are not using Tailwind, you are free to define your own views to render these links. When calling the `links` method on a paginator instance, pass the view name as the first argument to the method: +By default, the views rendered to display the pagination links are compatible with the [Tailwind CSS](https://tailwindcss.com) framework. However, if you are not using Tailwind, you are free to define your own views to render these links. When calling the `links` method on a paginator instance, you may pass the view name as the first argument to the method: {{ $paginator->links('view.name') }} - // Passing data to the view... + // Passing additional 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 -This command will place the views in the `resources/views/vendor/pagination` directory. The `tailwind.blade.php` file within this directory corresponds to the default pagination view. You may edit this file to modify the pagination HTML. +This command will place the views in your application's `resources/views/vendor/pagination` directory. The `tailwind.blade.php` file within this directory corresponds to the default pagination view. You may edit this file to modify the pagination HTML. -If you would like to designate a different file as the default pagination view, you may use the paginator's `defaultView` and `defaultSimpleView` methods within your `AppServiceProvider`: +If you would like to designate a different file as the default pagination view, you may invoke the paginator's `defaultView` and `defaultSimpleView` methods within the `boot` method of your `App\Providers\AppServiceProvider` class: + + ### Using Bootstrap -Laravel includes pagination views built using [Bootstrap CSS](https://getbootstrap.com/). To use these views instead of the default Tailwind views, you may call the paginator's `useBootstrap` method within your `AppServiceProvider`: +Laravel includes pagination views built using [Bootstrap CSS](https://getbootstrap.com/). To use these views instead of the default Tailwind views, you may call the paginator's `useBootstrap` method within the `boot` method of your `App\Providers\AppServiceProvider` class: use Illuminate\Pagination\Paginator; + /** + * Bootstrap any application services. + * + * @return void + */ public function boot() { Paginator::useBootstrap(); From 867f930794b366af2b50c6665b980c2ee6d2ccde Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 11:28:09 -0600 Subject: [PATCH 164/274] wip --- pagination.md | 69 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/pagination.md b/pagination.md index fb3be7f062..d15c4ebd68 100644 --- a/pagination.md +++ b/pagination.md @@ -5,7 +5,9 @@ - [Paginating Query Builder Results](#paginating-query-builder-results) - [Paginating Eloquent Results](#paginating-eloquent-results) - [Manually Creating A Paginator](#manually-creating-a-paginator) + - [Customizing Pagination URLs](#customizing-pagination-urls) - [Displaying Pagination Results](#displaying-pagination-results) + - [Adjusting The Pagination Link Window](#adjusting-the-pagination-link-window) - [Converting Results To JSON](#converting-results-to-json) - [Customizing The Pagination View](#customizing-the-pagination-view) - [Using Bootstrap](#using-bootstrap) @@ -14,7 +16,9 @@ ## Introduction -In other frameworks, pagination can be very painful. We hope Laravel's approach to pagination will be a breath of fresh air. 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 records with zero configuration. By default, the HTML generated by the paginator is compatible with the [Tailwind CSS framework](https://tailwindcss.com/); however, Bootstrap pagination support is also available. +In other frameworks, pagination can be very painful. We hope Laravel's approach to pagination will be a breath of fresh air. 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 records with zero configuration. + +By default, the HTML generated by the paginator is compatible with the [Tailwind CSS framework](https://tailwindcss.com/); however, Bootstrap pagination support is also available. ## Basic Usage @@ -85,57 +89,68 @@ In other words, the `Paginator` corresponds to the `simplePaginate` method on th > {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 + +### Customizing Pagination URLs -When calling the `paginate` method, you will receive an instance of `Illuminate\Pagination\LengthAwarePaginator`. When calling the `simplePaginate` method, you will receive an instance of `Illuminate\Pagination\Paginator`. These objects provide several methods that describe the result set. In addition to these helpers methods, the paginator instances are iterators and may be looped as an array. So, once you have retrieved the results, you may display the results and render the page links using [Blade](/docs/{{version}}/blade): +By default, links generated by the paginator will match the current request's URI. However, the paginator's `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/admin/users?page=N`, you should pass `/admin/users` to the `withPath` method: -```html -
- @foreach ($users as $user) - {{ $user->name }} - @endforeach -
+ use App\Models\User; -{{ $users->links() }} -``` + Route::get('/users', function () { + $users = User::paginate(15); -The `links` method will render the links to the rest of the pages in the result set. Each of these links will already contain the proper `page` query string variable. Remember, the HTML generated by the `links` method is compatible with the [Tailwind CSS framework](https://tailwindcss.com). + $users->withPath('/admin/users'); + + // + }); - -#### Customizing The Paginator URI + +#### Appending Query String Values -The paginator's `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/admin/users?page=N`, you should pass `/admin/users` to the `withPath` method: +You may append to the query string of pagination links using the `appends` method. For example, to append `sort=votes` to each pagination link, you should make the following call to `appends`: use App\Models\User; Route::get('/users', function () { $users = User::paginate(15); - $users->withPath('/admin/users'); + $users->appends(['sort' => 'votes']); // }); - -#### Appending To Pagination Links +You may use the `withQueryString` method if you would like to append all of the current request's query string values to the pagination links: -You may append to the query string of pagination links using the `appends` method. For example, to append `sort=votes` to each pagination link, you should make the following call to `appends`: + $users = User::paginate(15)->withQueryString(); - {{ $users->appends(['sort' => 'votes'])->links() }} + +#### Appending Hash Fragments -You may use the `withQueryString` method if you would like to append all of the current request's query string values to the pagination links: +If you need to append a "hash fragment" to URLs generated by the paginator, you may use the `fragment` method. For example, to append `#users` to the end of each pagination link, you should invoke the `fragment` method like so: + + $users = User::paginate(15)->fragment('users'); + + +## Displaying Pagination Results + +When calling the `paginate` method, you will receive an instance of `Illuminate\Pagination\LengthAwarePaginator`. When calling the `simplePaginate` method, you will receive an instance of `Illuminate\Pagination\Paginator`. These objects provide several methods that describe the result set. In addition to these helpers methods, the paginator instances are iterators and may be looped as an array. So, once you have retrieved the results, you may display the results and render the page links using [Blade](/docs/{{version}}/blade): - {{ $users->withQueryString()->links() }} +```html +
+ @foreach ($users as $user) + {{ $user->name }} + @endforeach +
-If you wish to append a "hash fragment" to the paginator's URLs, you may use the `fragment` method. For example, to append `#users` to the end of each pagination link, you may invoke the `fragment` method like so: +{{ $users->links() }} +``` - {{ $users->fragment('users')->links() }} +The `links` method will render the links to the rest of the pages in the result set. Each of these links will already contain the proper `page` query string variable. Remember, the HTML generated by the `links` method is compatible with the [Tailwind CSS framework](https://tailwindcss.com). -#### Adjusting The Pagination Link Window +### Adjusting The Pagination Link Window -You may control how many additional links are displayed on each side of the paginator's URL "window". By default, three links are displayed on each side of the primary paginator links. However, you may control this number using the `onEachSide` method: +When the paginator displays pagination links, the current page number is displayed as well as links for the three pages before and after the current page. If needed, you may control how many additional links are displayed on each side of the current page using the `onEachSide` method: {{ $users->onEachSide(5)->links() }} From 0f16d1417e5862e826afd6174226ac2d2b7edb6c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 11:41:52 -0600 Subject: [PATCH 165/274] wip --- filesystem.md | 12 ++++++------ localization.md | 2 +- migrations.md | 26 ++++++++++++-------------- mix.md | 2 +- requests.md | 4 ++-- responses.md | 6 +++--- valet.md | 2 +- 7 files changed, 26 insertions(+), 28 deletions(-) diff --git a/filesystem.md b/filesystem.md index 3942801ab5..3e49433cfa 100644 --- a/filesystem.md +++ b/filesystem.md @@ -175,7 +175,7 @@ The `missing` method may be used to determine if a file is missing from the disk ### Downloading Files -The `download` method may be used to generate a response that forces the user's browser to download the file at the given path. The `download` method accepts a file name as the second argument to the method, which will determine the file name that is seen by the user downloading the file. Finally, you may pass an array of HTTP headers as the third argument to the method: +The `download` method may be used to generate a response that forces the user's browser to download the file at the given path. The `download` method accepts a filename as the second argument to the method, which will determine the filename that is seen by the user downloading the file. Finally, you may pass an array of HTTP headers as the third argument to the method: return Storage::download('file.jpg'); @@ -269,13 +269,13 @@ Streaming files to storage offers significantly reduced memory usage. If you wou use Illuminate\Http\File; use Illuminate\Support\Facades\Storage; - // Automatically generate a unique ID for file name... + // Automatically generate a unique ID for filename... $path = Storage::putFile('photos', new File('/path/to/photo')); - // Manually specify a file name... + // Manually specify a filename... $path = 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 and not a file name. By default, the `putFile` method will generate a unique ID to serve as the file name. The file's extension will be determined by examining the file's MIME type. 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. +There are a few important things to note about the `putFile` method. Note that we only specified a directory name and not a filename. By default, the `putFile` method will generate a unique ID to serve as the filename. The file's extension will be determined by examining the file's MIME type. The path to the file will be returned by the `putFile` method so you can store the path, including the generated filename, 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 Amazon S3 and would like the file to be publicly accessible via generated URLs: @@ -327,7 +327,7 @@ 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 generate a unique ID to serve as the file name. The file's extension will be determined by examining the file's MIME type. 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 filename. By default, the `store` method will generate a unique ID to serve as the filename. The file's extension will be determined by examining the file's MIME type. The path to the file will be returned by the `store` method so you can store the path, including the generated filename, in your database. You may also call the `putFile` method on the `Storage` facade to perform the same file storage operation as the example above: @@ -336,7 +336,7 @@ You may also call the `putFile` method on the `Storage` facade to perform the sa #### Specifying A File Name -If you do not want a file name to be automatically assigned to your stored file, you may use the `storeAs` method, which receives the path, the file name, and the (optional) disk as its arguments: +If you do not want a filename to be automatically assigned to your stored file, you may use the `storeAs` method, which receives the path, the filename, and the (optional) disk as its arguments: $path = $request->file('avatar')->storeAs( 'avatars', $request->user()->id diff --git a/localization.md b/localization.md index 5ea69fd51c..d7bc1b68e7 100644 --- a/localization.md +++ b/localization.md @@ -111,7 +111,7 @@ For this reason, Laravel also provides support for defining translation strings #### Key / File Conflicts -You should not define translation string keys that conflict with other translation file names. For example, translating `__('Action')` for the "NL" locale while a `nl/action.php` file exists but a `nl.json` file does not exist will result in the translator returning the contents of `nl/action.php`. +You should not define translation string keys that conflict with other translation filenames. For example, translating `__('Action')` for the "NL" locale while a `nl/action.php` file exists but a `nl.json` file does not exist will result in the translator returning the contents of `nl/action.php`. ## Retrieving Translation Strings diff --git a/migrations.md b/migrations.md index e5eb8f7769..de0dd5a85a 100644 --- a/migrations.md +++ b/migrations.md @@ -23,44 +23,42 @@ ## Introduction -Migrations are like version control for your database, allowing your team to modify and share the application's database schema. Migrations are typically paired with Laravel's schema builder to build your application's database schema. If you have ever had to tell a teammate to manually add a column to their local database schema, you've faced the problem that database migrations solve. +Migrations are like version control for your database, allowing your team to define and share the application's database schema definition. If you have ever had to tell a teammate to manually add a column to their local database schema after pulling in your changes from source control, you've faced the problem that database migrations solve. -The Laravel `Schema` [facade](/docs/{{version}}/facades) provides database agnostic support for creating and manipulating tables across all of Laravel's supported database systems. +The Laravel `Schema` [facade](/docs/{{version}}/facades) provides database agnostic support for creating and manipulating tables across all of Laravel's supported database systems. Typically, migrations will use this facade to create and modify database tables and columns. ## Generating Migrations -To create a migration, use the `make:migration` [Artisan command](/docs/{{version}}/artisan): +You may use the `make:migration` [Artisan command](/docs/{{version}}/artisan) to generate a database migration. The new migration will be placed in your `database/migrations` directory. Each migration filename contains a timestamp that allows Laravel to determine the order of the migrations: - php artisan make:migration create_users_table + php artisan make:migration create_flights_table -The new migration will be placed in your `database/migrations` directory. Each migration file name contains a timestamp, which allows Laravel to determine the order of the migrations. +The `--table` and `--create` options may be used to indicate the name of the table and whether or not the migration will be creating a new table. These options pre-fill the generated migration file with the specified table: -> {tip} Migration stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization) - -The `--table` and `--create` options may also be used to indicate the name of the table and whether or not the migration will be creating a new table. These options pre-fill the generated migration stub file with the specified table: + php artisan make:migration create_flights_table --create=flights - php artisan make:migration create_users_table --create=users + php artisan make:migration add_destination_to_flights_table --table=flights - php artisan make:migration add_votes_to_users_table --table=users +If you would like to specify a custom path for the generated migration, you may use the `--path` option when executing the `make:migration` command. The given path should be relative to your application's base path. -If you would like to specify a custom output path for the generated migration, you may use the `--path` option when executing the `make:migration` command. The given path should be relative to your application's base path. +> {tip} Migration stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization) ### Squashing Migrations -As you build your application, you may accumulate more and more migrations over time. This can lead to your migration directory becoming bloated with potentially hundreds of migrations. If you would like, you may "squash" your migrations into a single SQL file. To get started, execute the `schema:dump` command: +As you build your application, you may accumulate more and more migrations over time. This can lead to your `database/migrations` directory becoming bloated with potentially hundreds of migrations. If you would like, you may "squash" your migrations into a single SQL file. To get started, execute the `schema:dump` command: php artisan schema:dump // Dump the current database schema and prune all existing migrations... php artisan schema:dump --prune -When you execute this command, Laravel will write a "schema" file to your `database/schema` directory. Now, when you attempt to migrate your database and no other migrations have been executed, Laravel will execute the schema file's SQL first. After executing the schema file's commands, Laravel will execute any remaining migrations that were not part of the schema dump. +When you execute this command, Laravel will write a "schema" file to your application's `database/schema` directory. Now, when you attempt to migrate your database and no other migrations have been executed, Laravel will execute the schema file's SQL statements first. After executing the schema file's statements, Laravel will execute any remaining migrations that were not part of the schema dump. You should commit your database schema file to source control so that other new developers on your team may quickly create your application's initial database structure. -> {note} Migration squashing is only available for the MySQL, PostgreSQL, and SQLite databases. However, database dumps may not be restored to in-memory SQLite databases. +> {note} Migration squashing is only available for the MySQL, PostgreSQL, and SQLite databases. However, schema dumps may not be restored to in-memory SQLite databases. ## Migration Structure diff --git a/mix.md b/mix.md index 78cd4ed50c..d924fe2090 100644 --- a/mix.md +++ b/mix.md @@ -311,7 +311,7 @@ The `version` method will append a unique hash to the filenames of all compiled mix.js('resources/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: +After generating the versioned file, you won't know the exact filename. 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: diff --git a/requests.md b/requests.md index a8f30922f8..aedddb6df7 100644 --- a/requests.md +++ b/requests.md @@ -411,7 +411,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 that will move an uploaded file to one of your disks, which may be a location on your local filesystem or 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 a unique ID will automatically be generated to serve as the file name. +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 filename, since a unique ID will automatically be generated to serve as the filename. 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: @@ -419,7 +419,7 @@ The `store` method also accepts an optional second argument for the name of the $path = $request->photo->store('images', 's3'); -If you do not want a file name to be automatically generated, you may use the `storeAs` method, which accepts the path, file name, and disk name as its arguments: +If you do not want a filename to be automatically generated, you may use the `storeAs` method, which accepts the path, filename, and disk name as its arguments: $path = $request->photo->storeAs('images', 'filename.jpg'); diff --git a/responses.md b/responses.md index 3402842ed0..ed363ffb44 100644 --- a/responses.md +++ b/responses.md @@ -275,18 +275,18 @@ If you would like to create a JSONP response, you may use the `json` method in c ### File Downloads -The `download` method may be used to generate a response that forces the user's browser to download the file at the given path. The `download` method accepts a file name as the second argument to the method, which will determine the file name that is seen by the user downloading the file. Finally, you may pass an array of HTTP headers as the third argument to the method: +The `download` method may be used to generate a response that forces the user's browser to download the file at the given path. The `download` method accepts a filename as the second argument to the method, which will determine the filename that is seen by the user downloading the file. Finally, you may pass an array of HTTP headers as the third argument to the method: return response()->download($pathToFile); return response()->download($pathToFile, $name, $headers); -> {note} Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII file name. +> {note} Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII filename. #### Streamed Downloads -Sometimes you may wish to turn the string response of a given operation into a downloadable response without having to write the contents of the operation to disk. You may use the `streamDownload` method in this scenario. This method accepts a callback, file name, and an optional array of headers as its arguments: +Sometimes you may wish to turn the string response of a given operation into a downloadable response without having to write the contents of the operation to disk. You may use the `streamDownload` method in this scenario. This method accepts a callback, filename, and an optional array of headers as its arguments: use App\Services\GitHub; diff --git a/valet.md b/valet.md index 4127bd7087..7608db5ace 100644 --- a/valet.md +++ b/valet.md @@ -221,7 +221,7 @@ You can write your own Valet "driver" to serve PHP applications running on anoth All three methods receive the `$sitePath`, `$siteName`, and `$uri` values as their arguments. The `$sitePath` is the fully qualified path to the site being served on your machine, such as `/Users/Lisa/Sites/my-project`. The `$siteName` is the "host" / "site name" portion of the domain (`my-project`). The `$uri` is the incoming request URI (`/foo/bar`). -Once you have completed your custom Valet driver, place it in the `~/.config/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`. +Once you have completed your custom Valet driver, place it in the `~/.config/valet/Drivers` directory using the `FrameworkValetDriver.php` naming convention. For example, if you are writing a custom valet driver for WordPress, your filename should be `WordPressValetDriver.php`. Let's take a look at a sample implementation of each method your custom Valet driver should implement. From 7fb81c4b814c8bb68ae8cf37b3287aabfde6e019 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 13:07:31 -0600 Subject: [PATCH 166/274] wip --- migrations.md | 62 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/migrations.md b/migrations.md index de0dd5a85a..f7380c5212 100644 --- a/migrations.md +++ b/migrations.md @@ -8,6 +8,7 @@ - [Rolling Back Migrations](#rolling-back-migrations) - [Tables](#tables) - [Creating Tables](#creating-tables) + - [Updating Tables](#updating-tables) - [Renaming / Dropping Tables](#renaming-and-dropping-tables) - [Columns](#columns) - [Creating Columns](#creating-columns) @@ -118,7 +119,7 @@ Some migration operations are destructive, which means they may cause you to los ### Rolling Back Migrations -To roll back the latest migration operation, you may use the `rollback` command. This command rolls back the last "batch" of migrations, which may include multiple migration files: +To roll back the latest migration operation, you may use the `rollback` Artisan command. This command rolls back the last "batch" of migrations, which may include multiple migration files: php artisan migrate:rollback @@ -163,8 +164,14 @@ The `migrate:fresh` command will drop all tables from the database and then exec To create a new database table, use the `create` method on the `Schema` facade. The `create` method accepts two arguments: the first is the name of the table, while the second is a closure which receives a `Blueprint` object that may be used to define the new table: + use Illuminate\Database\Schema\Blueprint; + use Illuminate\Support\Facades\Schema; + Schema::create('users', function (Blueprint $table) { $table->id(); + $table->string('name'); + $table->string('email'); + $table->timestamps(); }); When creating the table, you may use any of the schema builder's [column methods](#creating-columns) to define the table's columns. @@ -175,23 +182,23 @@ When creating the table, you may use any of the schema builder's [column methods You may check for the existence of a table or column using the `hasTable` and `hasColumn` methods: if (Schema::hasTable('users')) { - // + // The "users" table exists... } if (Schema::hasColumn('users', 'email')) { - // + // The "users" table exists and has an "email" column... } #### Database Connection & Table Options -If you want to perform a schema operation on a database connection that is not your default connection, use the `connection` method: +If you want to perform a schema operation on a database connection that is not your application's default connection, use the `connection` method: - Schema::connection('foo')->create('users', function (Blueprint $table) { + Schema::connection('sqlite')->create('users', function (Blueprint $table) { $table->id(); }); -You may use the following commands on the schema builder to define the table's options: +You may use the following properties and methods on the schema builder to define the table's options: Command | Description ------- | ----------- @@ -200,11 +207,25 @@ Command | Description `$table->collation = 'utf8mb4_unicode_ci';` | Specify a default collation for the table (MySQL). `$table->temporary();` | Create a temporary table (except SQL Server). + +### Updating Tables + +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 or indexes to the table: + + use Illuminate\Database\Schema\Blueprint; + use Illuminate\Support\Facades\Schema; + + Schema::table('users', function (Blueprint $table) { + $table->integer('votes'); + }); + ### Renaming / Dropping Tables To rename an existing database table, use the `rename` method: + use Illuminate\Support\Facades\Schema; + Schema::rename($from, $to); To drop an existing table, you may use the `drop` or `dropIfExists` methods: @@ -224,16 +245,19 @@ Before renaming a table, you should verify that any foreign key constraints on t ### Creating Columns -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: +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 an `Illuminate\Database\Schema\Blueprint` instance you may use to add columns to the table: + + use Illuminate\Database\Schema\Blueprint; + use Illuminate\Support\Facades\Schema; Schema::table('users', function (Blueprint $table) { - $table->string('email'); + $table->integer('votes'); }); #### Available Column Types -The schema builder contains a variety of column types that you may specify when building your tables: +The schema builder blueprint offers a variety of methods that correspond to the different types of columns you can add to your database tables. Each of the available methods are listed in the table below: Command | Description ------- | ----------- @@ -302,13 +326,13 @@ Command | Description ### Column Modifiers -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: +In addition to the column types listed above, there are several column "modifiers" you may use when adding a column to a database table. For example, to make the column "nullable", you may use the `nullable` method: Schema::table('users', function (Blueprint $table) { $table->string('email')->nullable(); }); -The following list contains all available column modifiers. This list does not include the [index modifiers](#creating-indexes): +The following table contains all of the available column modifiers. This list does not include [index modifiers](#creating-indexes): Modifier | Description -------- | ----------- @@ -332,7 +356,7 @@ Modifier | Description #### Default Expressions -The `default` modifier accepts a value or an `\Illuminate\Database\Query\Expression` instance. Using an `Expression` instance will prevent wrapping the value in quotes and allow you to use database specific functions. One situation where this is particularly useful is when you need to assign default values to JSON columns: +The `default` modifier accepts a value or an `Illuminate\Database\Query\Expression` instance. Using an `Expression` instance will prevent Laravel from wrapping the value in quotes and allow you to use database specific functions. One situation where this is particularly useful is when you need to assign default values to JSON columns: {note} Support for default expressions depends on your database driver, database version, and the field type. Please refer to the appropriate documentation for compatibility. Also note that using database specific functions may tightly couple you to a specific driver. +> {note} Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. ### Modifying Columns @@ -366,14 +390,14 @@ The `default` modifier accepts a value or an `\Illuminate\Database\Query\Express #### Prerequisites -Before modifying a column, be sure to add the `doctrine/dbal` dependency to your `composer.json` file. The Doctrine DBAL library is used to determine the current state of the column and create the SQL queries needed to make the required adjustments: +Before modifying a column, you must install the `doctrine/dbal` package using the Composer package manager. The Doctrine DBAL library is used to determine the current state of the column and to create the SQL queries needed to make the requested changes to your column: composer require doctrine/dbal #### Updating Column Attributes -The `change` method allows you to modify type and attributes of existing columns. 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 the type and attributes of existing columns. 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. To accomplish this, we simply define the new state of the column and then call the `change` method: Schema::table('users', function (Blueprint $table) { $table->string('name', 50)->change(); @@ -385,12 +409,12 @@ We could also modify a column to be nullable: $table->string('name', 50)->nullable()->change(); }); -> {note} Only the following column types can be "changed": bigInteger, binary, boolean, date, dateTime, dateTimeTz, decimal, integer, json, longText, mediumText, smallInteger, string, text, time, unsignedBigInteger, unsignedInteger, unsignedSmallInteger and uuid. +> {note} The following column types can be modified: `bigInteger`, `binary`, `boolean`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `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: +To rename a column, you may use the `renameColumn` method provided by the schema builder blueprint. Before renaming a column, ensure that you have installed the `doctrine/dbal` library via the Composer package manager: Schema::table('users', function (Blueprint $table) { $table->renameColumn('from', 'to'); @@ -401,7 +425,7 @@ To rename a column, you may use the `renameColumn` method on the schema builder. ### 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: +To drop a column, you may use the `dropColumn` method on the schema builder blueprint. If your application is utilizing an SQLite database, you must install the `doctrine/dbal` package via the Composer package manager before the `dropColumn` method may be used: Schema::table('users', function (Blueprint $table) { $table->dropColumn('votes'); @@ -418,6 +442,8 @@ You may drop multiple columns from a table by passing an array of column names t #### Available Command Aliases +Laravel provides several convenient methods related to dropping common types of columns. Each of these methods is described in the table below: + Command | Description ------- | ----------- `$table->dropMorphs('morphable');` | Drop the `morphable_id` and `morphable_type` columns. From 63459f860caa0b1dc9a2fd8bb953ef7e944ce95d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 13:21:01 -0600 Subject: [PATCH 167/274] wip --- migrations.md | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/migrations.md b/migrations.md index f7380c5212..2c7ce70c54 100644 --- a/migrations.md +++ b/migrations.md @@ -461,9 +461,14 @@ Command | Description The Laravel schema builder supports several types of indexes. The following example creates a new `email` column and specifies that its values should be unique. To create the index, we can chain the `unique` method onto the column definition: - $table->string('email')->unique(); + use Illuminate\Database\Schema\Blueprint; + use Illuminate\Support\Facades\Schema; + + Schema::table('users', function (Blueprint $table) { + $table->string('email')->unique(); + }); -Alternatively, you may create the index after defining the column. For example: +Alternatively, you may create the index after defining the column. To do so, you should call the `unique` method on the schema builder blueprint. This methods accepts the name of the column that should receive a unique index: $table->unique('email'); @@ -471,27 +476,27 @@ You may even pass an array of columns to an index method to create a compound (o $table->index(['account_id', 'created_at']); -Laravel will automatically generate an index name based on the table, column names, and the index type, but you may pass a second argument to the method to specify the index name yourself: +When creating an index, Laravel will automatically generate an index name based on the table, column names, and the index type, but you may pass a second argument to the method to specify the index name yourself: $table->unique('email', 'unique_email'); #### Available Index Types -Each index method accepts an optional second argument to specify the name of the index. If omitted, the name will be derived from the names of the table and column(s) used for the index, as well as the index type. +Laravel's schema builder blueprint class provides methods for creating each type of index supported by Laravel. Each index method accepts an optional second argument to specify the name of the index. If omitted, the name will be derived from the names of the table and column(s) used for the index, as well as the index type. Each of the available index methods are described in the table below: Command | Description ------- | ----------- `$table->primary('id');` | Adds a primary key. `$table->primary(['id', 'parent_id']);` | Adds composite keys. `$table->unique('email');` | Adds a unique index. -`$table->index('state');` | Adds a plain index. -`$table->spatialIndex('location');` | Adds a spatial index. (except SQLite) +`$table->index('state');` | Adds an index. +`$table->spatialIndex('location');` | Adds a spatial index (except SQLite). #### 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`: +By default, Laravel uses the `utf8mb4` character set. 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 the default string length by calling the `Schema::defaultStringLength` method within the `boot` method of your `App\Providers\AppServiceProvider` class: use Illuminate\Support\Facades\Schema; @@ -510,7 +515,7 @@ Alternatively, you may enable the `innodb_large_prefix` option for your database ### Renaming Indexes -To rename an index, you may use the `renameIndex` method. This method accepts the current index name as its first argument and the desired new name as its second argument: +To rename an index, you may use the `renameIndex` method provided by the schema builder blueprint. This method accepts the current index name as its first argument and the desired name as its second argument: $table->renameIndex('from', 'to') @@ -526,7 +531,7 @@ Command | Description `$table->dropIndex('geo_state_index');` | Drop a basic index from the "geo" table. `$table->dropSpatialIndex('geo_location_spatialindex');` | Drop a spatial index from the "geo" table (except SQLite). -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: +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 index type: Schema::table('geo', function (Blueprint $table) { $table->dropIndex(['state']); // Drops index 'geo_state_index' @@ -537,19 +542,22 @@ 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: + use Illuminate\Database\Schema\Blueprint; + use Illuminate\Support\Facades\Schema; + Schema::table('posts', function (Blueprint $table) { $table->unsignedBigInteger('user_id'); $table->foreign('user_id')->references('id')->on('users'); }); -Since this syntax is rather verbose, Laravel provides additional, terser methods that use convention to provide a better developer experience. The example above could be written like so: +Since this syntax is rather verbose, Laravel provides additional, terser methods that use conventions to provide a better developer experience. The example above can be rewritten like so: Schema::table('posts', function (Blueprint $table) { $table->foreignId('user_id')->constrained(); }); -The `foreignId` method is an alias for `unsignedBigInteger` while the `constrained` method will use convention to determine the table and column name being referenced. If your table name does not match the convention, you may specify the table name by passing it as an argument to the `constrained` method: +The `foreignId` method is an alias for `unsignedBigInteger` while the `constrained` method will use conventions to determine the table and column name being referenced. If your table name does not match Laravel's conventions, you may specify the table name by passing it as an argument to the `constrained` method: Schema::table('posts', function (Blueprint $table) { $table->foreignId('user_id')->constrained('users'); @@ -562,20 +570,26 @@ You may also specify the desired action for the "on delete" and "on update" prop ->constrained() ->onDelete('cascade'); -Any additional [column modifiers](#column-modifiers) must be called before `constrained`: +Any additional [column modifiers](#column-modifiers) must be called before the `constrained` method: $table->foreignId('user_id') ->nullable() ->constrained(); -To drop a foreign key, you may use the `dropForeign` method, passing the foreign key constraint to be deleted as an argument. Foreign key constraints use the same naming convention as indexes, based on the table name and the columns in the constraint, followed by a "\_foreign" suffix: + +#### Dropping Foreign Keys + +To drop a foreign key, you may use the `dropForeign` method, passing the name of the foreign key constraint to be deleted as an argument. Foreign key constraints use the same naming convention as indexes. In other words, the foreign key constraint name is based on the name of the table and the columns in the constraint, followed by a "\_foreign" suffix: $table->dropForeign('posts_user_id_foreign'); -Alternatively, you may pass an array containing the column name that holds the foreign key to the `dropForeign` method. The array will be automatically converted using the constraint name convention used by Laravel's schema builder: +Alternatively, you may pass an array containing the column name that holds the foreign key to the `dropForeign` method. The array will be converted to a foreign key constraint name using Laravel's constraint naming conventions: $table->dropForeign(['user_id']); + +#### Toggling Foreign Key Constraints + You may enable or disable foreign key constraints within your migrations by using the following methods: Schema::enableForeignKeyConstraints(); From 41849a8c1a06c94948cb6fce83431cc1cb5644c7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 14:07:48 -0600 Subject: [PATCH 168/274] wip --- migrations.md | 571 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 507 insertions(+), 64 deletions(-) diff --git a/migrations.md b/migrations.md index 2c7ce70c54..e1bd0a7b41 100644 --- a/migrations.md +++ b/migrations.md @@ -12,6 +12,7 @@ - [Renaming / Dropping Tables](#renaming-and-dropping-tables) - [Columns](#columns) - [Creating Columns](#creating-columns) + - [Available Column Types](#available-column-types) - [Column Modifiers](#column-modifiers) - [Modifying Columns](#modifying-columns) - [Dropping Columns](#dropping-columns) @@ -255,73 +256,515 @@ The `table` method on the `Schema` facade may be used to update existing tables. }); -#### Available Column Types +### Available Column Types The schema builder blueprint offers a variety of methods that correspond to the different types of columns you can add to your database tables. Each of the available methods are listed in the table below: -Command | Description -------- | ----------- -`$table->id();` | Alias of `$table->bigIncrements('id')`. -`$table->foreignId('user_id');` | Alias of `$table->unsignedBigInteger('user_id')`. -`$table->bigIncrements('id');` | Auto-incrementing UNSIGNED BIGINT (primary key) equivalent column. -`$table->bigInteger('votes');` | BIGINT equivalent column. -`$table->binary('data');` | BLOB equivalent column. -`$table->boolean('confirmed');` | BOOLEAN equivalent column. -`$table->char('name', 100);` | CHAR equivalent column with a length. -`$table->date('created_at');` | DATE equivalent column. -`$table->dateTime('created_at', 0);` | DATETIME equivalent column with precision (total digits). -`$table->dateTimeTz('created_at', 0);` | DATETIME (with timezone) equivalent column with precision (total digits). -`$table->decimal('amount', 8, 2);` | DECIMAL equivalent column with precision (total digits) and scale (decimal digits). -`$table->double('amount', 8, 2);` | DOUBLE equivalent column with precision (total digits) and scale (decimal digits). -`$table->enum('level', ['easy', 'hard']);` | ENUM equivalent column. -`$table->float('amount', 8, 2);` | FLOAT equivalent column with a precision (total digits) and scale (decimal digits). -`$table->geometry('positions');` | GEOMETRY equivalent column. -`$table->geometryCollection('positions');` | GEOMETRYCOLLECTION equivalent column. -`$table->increments('id');` | Auto-incrementing UNSIGNED INTEGER (primary key) equivalent column. -`$table->integer('votes');` | INTEGER equivalent column. -`$table->ipAddress('visitor');` | IP address equivalent column. -`$table->json('options');` | JSON equivalent column. -`$table->jsonb('options');` | JSONB equivalent column. -`$table->lineString('positions');` | LINESTRING equivalent column. -`$table->longText('description');` | LONGTEXT equivalent column. -`$table->macAddress('device');` | MAC address equivalent column. -`$table->mediumIncrements('id');` | Auto-incrementing UNSIGNED MEDIUMINT (primary key) equivalent column. -`$table->mediumInteger('votes');` | MEDIUMINT equivalent column. -`$table->mediumText('description');` | MEDIUMTEXT equivalent column. -`$table->morphs('taggable');` | Adds `taggable_id` UNSIGNED BIGINT and `taggable_type` VARCHAR equivalent columns. -`$table->uuidMorphs('taggable');` | Adds `taggable_id` CHAR(36) and `taggable_type` VARCHAR(255) UUID equivalent columns. -`$table->multiLineString('positions');` | MULTILINESTRING equivalent column. -`$table->multiPoint('positions');` | MULTIPOINT equivalent column. -`$table->multiPolygon('positions');` | MULTIPOLYGON equivalent column. -`$table->nullableMorphs('taggable');` | Adds nullable versions of `morphs()` columns. -`$table->nullableUuidMorphs('taggable');` | Adds nullable versions of `uuidMorphs()` columns. -`$table->nullableTimestamps(0);` | Alias of `timestamps()` method. -`$table->point('position');` | POINT equivalent column. -`$table->polygon('positions');` | POLYGON equivalent column. -`$table->rememberToken();` | Adds a nullable `remember_token` VARCHAR(100) equivalent column. -`$table->set('flavors', ['strawberry', 'vanilla']);` | SET equivalent column. -`$table->smallIncrements('id');` | Auto-incrementing UNSIGNED SMALLINT (primary key) equivalent column. -`$table->smallInteger('votes');` | SMALLINT equivalent column. -`$table->softDeletes('deleted_at', 0);` | Adds a nullable `deleted_at` TIMESTAMP equivalent column for soft deletes with precision (total digits). -`$table->softDeletesTz('deleted_at', 0);` | Adds a nullable `deleted_at` TIMESTAMP (with timezone) equivalent column for soft deletes with precision (total digits). -`$table->string('name', 100);` | VARCHAR equivalent column with a length. -`$table->text('description');` | TEXT equivalent column. -`$table->time('sunrise', 0);` | TIME equivalent column with precision (total digits). -`$table->timeTz('sunrise', 0);` | TIME (with timezone) equivalent column with precision (total digits). -`$table->timestamp('added_on', 0);` | TIMESTAMP equivalent column with precision (total digits). -`$table->timestampTz('added_on', 0);` | TIMESTAMP (with timezone) equivalent column with precision (total digits). -`$table->timestamps(0);` | Adds nullable `created_at` and `updated_at` TIMESTAMP equivalent columns with precision (total digits). -`$table->timestampsTz(0);` | Adds nullable `created_at` and `updated_at` TIMESTAMP (with timezone) equivalent columns with precision (total digits). -`$table->tinyIncrements('id');` | Auto-incrementing UNSIGNED TINYINT (primary key) equivalent column. -`$table->tinyInteger('votes');` | TINYINT equivalent column. -`$table->unsignedBigInteger('votes');` | UNSIGNED BIGINT equivalent column. -`$table->unsignedDecimal('amount', 8, 2);` | UNSIGNED DECIMAL equivalent column with a precision (total digits) and scale (decimal digits). -`$table->unsignedInteger('votes');` | UNSIGNED INTEGER equivalent column. -`$table->unsignedMediumInteger('votes');` | UNSIGNED MEDIUMINT equivalent column. -`$table->unsignedSmallInteger('votes');` | UNSIGNED SMALLINT equivalent column. -`$table->unsignedTinyInteger('votes');` | UNSIGNED TINYINT equivalent column. -`$table->uuid('id');` | UUID equivalent column. -`$table->year('birth_year');` | YEAR equivalent column. + + +
+[bigIncrements](#column-method-bigIncrements) +[bigInteger](#column-method-bigInteger) +[binary](#column-method-binary) +[boolean](#column-method-boolean) +[char](#column-method-char) +[dateTimeTz](#column-method-dateTimeTz) +[dateTime](#column-method-dateTime) +[date](#column-method-date) +[decimal](#column-method-decimal) +[double](#column-method-double) +[enum](#column-method-enum) +[float](#column-method-float) +[foreignId](#column-method-foreignId) +[geometryCollection](#column-method-geometryCollection) +[geometry](#column-method-geometry) +[id](#column-method-id) +[increments](#column-method-increments) +[integer](#column-method-integer) +[ipAddress](#column-method-ipAddress) +[json](#column-method-json) +[jsonb](#column-method-jsonb) +[lineString](#column-method-lineString) +[longText](#column-method-longText) +[macAddress](#column-method-macAddress) +[mediumIncrements](#column-method-mediumIncrements) +[mediumInteger](#column-method-mediumInteger) +[mediumText](#column-method-mediumText) +[morphs](#column-method-morphs) +[multiLineString](#column-method-multiLineString) +[multiPoint](#column-method-multiPoint) +[multiPolygon](#column-method-multiPolygon) +[nullableMorphs](#column-method-nullableMorphs) +[nullableTimestamps](#column-method-nullableTimestamps) +[nullableUuidMorphs](#column-method-nullableUuidMorphs) +[point](#column-method-point) +[polygon](#column-method-polygon) +[rememberToken](#column-method-rememberToken) +[set](#column-method-set) +[smallIncrements](#column-method-smallIncrements) +[smallInteger](#column-method-smallInteger) +[softDeletesTz](#column-method-softDeletesTz) +[softDeletes](#column-method-softDeletes) +[string](#column-method-string) +[text](#column-method-text) +[timeTz](#column-method-timeTz) +[time](#column-method-time) +[timestampTz](#column-method-timestampTz) +[timestamp](#column-method-timestamp) +[timestampsTz](#column-method-timestampsTz) +[timestamps](#column-method-timestamps) +[tinyIncrements](#column-method-tinyIncrements) +[tinyInteger](#column-method-tinyInteger) +[unsignedBigInteger](#column-method-unsignedBigInteger) +[unsignedDecimal](#column-method-unsignedDecimal) +[unsignedInteger](#column-method-unsignedInteger) +[unsignedMediumInteger](#column-method-unsignedMediumInteger) +[unsignedSmallInteger](#column-method-unsignedSmallInteger) +[unsignedTinyInteger](#column-method-unsignedTinyInteger) +[uuidMorphs](#column-method-uuidMorphs) +[uuid](#column-method-uuid) +[year](#column-method-year) +
+ + +#### `bigIncrements()` {#collection-method .first-collection-method} + +The `bigIncrements` method creates an auto-incrementing `UNSIGNED BIGINT` (primary key) equivalent column: + + $table->bigIncrements('id'); + + +#### `bigInteger()` {#collection-method} + +The `bigInteger` method creates a `BIGINT` equivalent column: + + $table->bigInteger('votes'); + + +#### `binary()` {#collection-method} + +The `binary` method creates a `BLOB` equivalent column: + + $table->binary('photo'); + + +#### `boolean()` {#collection-method} + +The `boolean` method creates a `BOOLEAN` equivalent column: + + $table->boolean('confirmed'); + + +#### `char()` {#collection-method} + +The `char` method creates a `CHAR` equivalent column with of a given length: + + $table->char('name', 100); + + +#### `dateTimeTz()` {#collection-method} + +The `dateTimeTz` method creates a `DATETIME` (with timezone) equivalent column with an optional precision (total digits): + + $table->dateTimeTz('created_at', $precision = 0); + + +#### `dateTime()` {#collection-method} + +The `dateTime` method creates a `DATETIME` equivalent column with an optional precision (total digits): + + $table->dateTime('created_at', $precision = 0); + + +#### `date()` {#collection-method} + +The `date` method creates a `DATE` equivalent column: + + $table->date('created_at'); + + +#### `decimal()` {#collection-method} + +The `decimal` method creates a `DECIMAL` equivalent column with the given precision (total digits) and scale (decimal digits): + + $table->decimal('amount', $precision = 8, $scale = 2); + + +#### `double()` {#collection-method} + +The `double` method creates a `DOUBLE` equivalent column with the given precision (total digits) and scale (decimal digits): + + $table->double('amount', 8, 2); + + +#### `enum()` {#collection-method} + +The `enum` method creates a `ENUM` equivalent column with the given valid values: + + $table->enum('difficulty', ['easy', 'hard']); + + +#### `float()` {#collection-method} + +The `float` method creates a `FLOAT` equivalent column with the given precision (total digits) and scale (decimal digits): + + $table->float('amount', 8, 2); + + +#### `foreignId()` {#collection-method} + +The `foreignId` method is an alias of the `unsignedBigInteger` method: + + $table->foreignId('user_id'); + + +#### `geometryCollection()` {#collection-method} + +The `geometryCollection` method creates a `GEOMETRYCOLLECTION` equivalent column: + + $table->geometryCollection('positions'); + + +#### `geometry()` {#collection-method} + +The `geometry` method creates a `GEOMETRY` equivalent column: + + $table->geometry('positions'); + + +#### `id()` {#collection-method} + +The `id` method is an alias of the `bigIncrements` method. By default, the method will create an `id` column; however, you may pass a column name if you would like to assign a different name to the column: + + $table->id(); + + +#### `increments()` {#collection-method} + +The `increments` method creates an auto-incrementing `UNSIGNED INTEGER` equivalent column as a primary key: + + $table->increments('id'); + + +#### `integer()` {#collection-method} + +The `integer` method creates an `INTEGER` equivalent column: + + $table->integer('votes'); + + +#### `ipAddress()` {#collection-method} + +The `ipAddress` method creates an `INTEGER` equivalent column: + + $table->ipAddress('visitor'); + + +#### `json()` {#collection-method} + +The `json` method creates an `JSON` equivalent column: + + $table->json('options'); + + +#### `jsonb()` {#collection-method} + +The `jsonb` method creates an `JSONB` equivalent column: + + $table->jsonb('options'); + + +#### `lineString()` {#collection-method} + +The `lineString` method creates an `LINESTRING` equivalent column: + + $table->lineString('positions'); + + +#### `longText()` {#collection-method} + +The `longText` method creates an `LONGTEXT` equivalent column: + + $table->longText('description'); + + +#### `macAddress()` {#collection-method} + +The `macAddress` method creates a column that is intended to hold a MAC address. Some database systems, such as PostgreSQL, have a dedicated column type for this type of data. Other database systems will use a string equivalent column: + + $table->macAddress('device'); + + +#### `mediumIncrements()` {#collection-method} + +The `mediumIncrements` method creates an auto-incrementing `UNSIGNED MEDIUMINT` equivalent column as a primary key: + + $table->mediumIncrements('id'); + + +#### `mediumInteger()` {#collection-method} + +The `mediumInteger` method creates an `MEDIUMINT` equivalent column: + + $table->mediumInteger('votes'); + + +#### `mediumText()` {#collection-method} + +The `mediumText` method creates an `MEDIUMTEXT` equivalent column: + + $table->mediumText('description'); + + +#### `morphs()` {#collection-method} + +The `morphs` method is a convenience method that adds a `{column}_id` `UNSIGNED BIGINT` equivalent column and a `{column}_type` `VARCHAR` equivalent column. + +This method is intended to be used when defining the columns necessary for a polymorphic [Eloquent relationship](/docs/{{version}}/eloquent-relationships). In the following example, `taggable_id` and `taggable_type` columns would be created: + + $table->morphs('taggable'); + + +#### `multiLineString()` {#collection-method} + +The `multiLineString` method creates an `MULTILINESTRING` equivalent column: + + $table->multiLineString('positions'); + + +#### `multiPoint()` {#collection-method} + +The `multiPoint` method creates an `MULTIPOINT` equivalent column: + + $table->multiPoint('positions'); + + +#### `multiPolygon()` {#collection-method} + +The `multiPolygon` method creates an `MULTIPOLYGON` equivalent column: + + $table->multiPolygon('positions'); + + +#### `nullableTimestamps()` {#collection-method} + +The method is similar to the [timestamps](#column-method-timestamps) method; however, the column that is created will be "nullable": + + $table->nullableTimestamps(0); + + +#### `nullableMorphs()` {#collection-method} + +The method is similar to the [morphs](#column-method-morphs) method; however, the columns that are created will be "nullable": + + $table->nullableMorphs('taggable'); + + +#### `nullableUuidMorphs()` {#collection-method} + +The method is similar to the [uuidMorphs](#column-method-uuidMorphs) method; however, the columns that are created will be "nullable": + + $table->nullableUuidMorphs('taggable'); + + +#### `point()` {#collection-method} + +The `point` method creates an `POINT` equivalent column: + + $table->point('position'); + + +#### `polygon()` {#collection-method} + +The `polygon` method creates an `POLYGON` equivalent column: + + $table->polygon('position'); + + +#### `rememberToken()` {#collection-method} + +The `rememberToken` method creates a nullable, `VARCHAR(100)` equivalent column that is intended to store the current "remember me" [authentication token](/docs/{{version}}/authentication#remembering-users): + + $table->rememberToken(); + + +#### `set()` {#collection-method} + +The `set` method creates an `SET` equivalent column with the given list of valid values: + + $table->set('flavors', ['strawberry', 'vanilla']); + + +#### `smallIncrements()` {#collection-method} + +The `smallIncrements` method creates an auto-incrementing `UNSIGNED SMALLINT` equivalent column as a primary key: + + $table->smallIncrements('id'); + + +#### `smallInteger()` {#collection-method} + +The `smallInteger` method creates an `SMALLINT` equivalent column: + + $table->smallInteger('votes'); + + +#### `softDeletesTz()` {#collection-method} + +The `softDeletesTz` method adds a nullable `deleted_at` `TIMESTAMP` (with timezone) equivalent column with an optional precision (total digits). This column is intended to store the `deleted_at` timestamp needed for Eloquent's "soft delete" functionality: + + $table->softDeletesTz($column = 'deleted_at', $precision = 0); + + +#### `softDeletes()` {#collection-method} + +The `softDeletes` method adds a nullable `deleted_at` `TIMESTAMP` equivalent column with an optional precision (total digits). This column is intended to store the `deleted_at` timestamp needed for Eloquent's "soft delete" functionality: + + $table->softDeletes($column = 'deleted_at', $precision = 0); + + +#### `string()` {#collection-method} + +The `string` method creates an `VARCHAR` equivalent column of the given length: + + $table->string('name', 100); + + +#### `text()` {#collection-method} + +The `text` method creates an `TEXT` equivalent column: + + $table->text('description'); + + +#### `timeTz()` {#collection-method} + +The `timeTz` method creates an `TIME` (with timezone) equivalent column with an optional precision (total digits): + + $table->timeTz('sunrise', $precision = 0); + + +#### `time()` {#collection-method} + +The `time` method creates an `TIME` equivalent column with an optional precision (total digits): + + $table->time('sunrise', $precision = 0); + + +#### `timestampTz()` {#collection-method} + +The `timestampTz` method creates an `TIMESTAMP` (with timezone) equivalent column with an optional precision (total digits): + + $table->timestampTz('added_at', $precision = 0); + + +#### `timestamp()` {#collection-method} + +The `timestamp` method creates an `TIMESTAMP` equivalent column with an optional precision (total digits): + + $table->timestamp('added_at', $precision = 0); + + +#### `timestampsTz()` {#collection-method} + +The `timestampsTz` method creates `created_at` and `updated_at` `TIMESTAMP` (with timezone) equivalent columns with an optional precision (total digits): + + $table->timestampsTz($precision = 0); + + +#### `timestamps()` {#collection-method} + +The `timestamps` method creates `created_at` and `updated_at` `TIMESTAMP` equivalent columns with an optional precision (total digits): + + $table->timestamps($precision = 0); + + +#### `tinyIncrements()` {#collection-method} + +The `tinyIncrements` method creates an auto-incrementing `UNSIGNED TINYINT` equivalent column as a primary key: + + $table->tinyIncrements('id'); + + +#### `tinyInteger()` {#collection-method} + +The `tinyInteger` method creates an `TINYINT` equivalent column: + + $table->tinyInteger('votes'); + + +#### `unsignedBigInteger()` {#collection-method} + +The `unsignedBigInteger` method creates an `UNSIGNED BIGINT` equivalent column: + + $table->unsignedBigInteger('votes'); + + +#### `unsignedDecimal()` {#collection-method} + +The `unsignedDecimal` method creates an `UNSIGNED DECIMAL` equivalent column with an optinoal precision (total digits) and scale (decimal digits): + + $table->unsignedDecimal('amount', $precision = 8, $scale = 2); + + +#### `unsignedInteger()` {#collection-method} + +The `unsignedInteger` method creates an `UNSIGNED INTEGER` equivalent column: + + $table->unsignedInteger('votes'); + + +#### `unsignedMediumInteger()` {#collection-method} + +The `unsignedMediumInteger` method creates an `UNSIGNED MEDIUMINT` equivalent column: + + $table->unsignedMediumInteger('votes'); + + +#### `unsignedSmallInteger()` {#collection-method} + +The `unsignedSmallInteger` method creates an `UNSIGNED SMALLINT` equivalent column: + + $table->unsignedSmallInteger('votes'); + + +#### `unsignedTinyInteger()` {#collection-method} + +The `unsignedTinyInteger` method creates an `UNSIGNED TINYINT` equivalent column: + + $table->unsignedTinyInteger('votes'); + + +#### `uuidMorphs()` {#collection-method} + +The `uuidMorphs` method is a convenience method that adds a `{column}_id` `CHAR(36)` equivalent column and a `{column}_type` `VARCHAR` equivalent column. + +This method is intended to be used when defining the columns necessary for a polymorphic [Eloquent relationship](/docs/{{version}}/eloquent-relationships) that use UUID identifiers. In the following example, `taggable_id` and `taggable_type` columns would be created: + + $table->uuidMorphs('taggable'); + + +#### `uuid()` {#collection-method} + +The `uuid` method creates an `UUID` equivalent column: + + $table->uuid('id'); + + +#### `year()` {#collection-method} + +The `year` method creates an `YEAR` equivalent column: + + $table->year('birth_year'); ### Column Modifiers From 45c7271a80be89ca3709a5ea8bb6057c34f6972b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 15:23:38 -0600 Subject: [PATCH 169/274] wip --- migrations.md | 65 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/migrations.md b/migrations.md index e1bd0a7b41..4a77db4775 100644 --- a/migrations.md +++ b/migrations.md @@ -199,14 +199,30 @@ If you want to perform a schema operation on a database connection that is not y $table->id(); }); -You may use the following properties and methods on the schema builder to define the table's options: +In addition, a few other properties and methods may be used to define other aspects of the table's creation. The `engine` property may be used to specify the table's storage engine when using MySQL: -Command | Description -------- | ----------- -`$table->engine = 'InnoDB';` | Specify the table storage engine (MySQL). -`$table->charset = 'utf8mb4';` | Specify a default character set for the table (MySQL). -`$table->collation = 'utf8mb4_unicode_ci';` | Specify a default collation for the table (MySQL). -`$table->temporary();` | Create a temporary table (except SQL Server). + Schema::create('users', function (Blueprint $table) { + $table->engine = 'InnoDB'; + + // ... + }); + +The `charset` and `collation` properties may be used to specify the character set and collation for the created table when using MySQL: + + Schema::create('users', function (Blueprint $table) { + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + + // ... + }); + +The `temporary` method may be used to indicate that the table should be "temporary". Temporary tables are only visible to the current connection's database session and are dropped automatically when the connection is closed: + + Schema::create('calculations', function (Blueprint $table) { + $table->temporary(); + + // ... + }); ### Updating Tables @@ -771,6 +787,9 @@ The `year` method creates an `YEAR` equivalent column: In addition to the column types listed above, there are several column "modifiers" you may use when adding a column to a database table. For example, to make the column "nullable", you may use the `nullable` method: + use Illuminate\Database\Schema\Blueprint; + use Illuminate\Support\Facades\Schema; + Schema::table('users', function (Blueprint $table) { $table->string('email')->nullable(); }); @@ -779,22 +798,22 @@ The following table contains all of the available column modifiers. This list do Modifier | Description -------- | ----------- -`->after('column')` | Place the column "after" another column (MySQL) -`->autoIncrement()` | Set INTEGER columns as auto-increment (primary key) -`->charset('utf8mb4')` | Specify a character set for the column (MySQL) -`->collation('utf8mb4_unicode_ci')` | Specify a collation for the column (MySQL/PostgreSQL/SQL Server) -`->comment('my comment')` | Add a comment to a column (MySQL/PostgreSQL) -`->default($value)` | Specify a "default" value for the column -`->first()` | Place the column "first" in the table (MySQL) -`->from($integer)` | Set the starting value of an auto-incrementing field (MySQL / PostgreSQL) -`->nullable($value = true)` | Allows (by default) NULL values to be inserted into the column -`->storedAs($expression)` | Create a stored generated column (MySQL) -`->unsigned()` | Set INTEGER columns as UNSIGNED (MySQL) -`->useCurrent()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP as default value -`->useCurrentOnUpdate()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP when a record is updated -`->virtualAs($expression)` | Create a virtual generated column (MySQL) -`->generatedAs($expression)` | Create an identity column with specified sequence options (PostgreSQL) -`->always()` | Defines the precedence of sequence values over input for an identity column (PostgreSQL) +`->after('column')` | Place the column "after" another column (MySQL). +`->autoIncrement()` | Set INTEGER columns as auto-incrementing (primary key). +`->charset('utf8mb4')` | Specify a character set for the column (MySQL). +`->collation('utf8mb4_unicode_ci')` | Specify a collation for the column (MySQL/PostgreSQL/SQL Server). +`->comment('my comment')` | Add a comment to a column (MySQL/PostgreSQL). +`->default($value)` | Specify a "default" value for the column. +`->first()` | Place the column "first" in the table (MySQL). +`->from($integer)` | Set the starting value of an auto-incrementing field (MySQL / PostgreSQL). +`->nullable($value = true)` | Allow NULL values to be inserted into the column. +`->storedAs($expression)` | Create a stored generated column (MySQL). +`->unsigned()` | Set INTEGER columns as UNSIGNED (MySQL). +`->useCurrent()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP as default value. +`->useCurrentOnUpdate()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP when a record is updated. +`->virtualAs($expression)` | Create a virtual generated column (MySQL). +`->generatedAs($expression)` | Create an identity column with specified sequence options (PostgreSQL). +`->always()` | Defines the precedence of sequence values over input for an identity column (PostgreSQL). #### Default Expressions From 8e8bc1ea97ef0c786488e6ae440f81c4ae6f4a87 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 15:32:35 -0600 Subject: [PATCH 170/274] wip --- migrations.md | 2 +- seeding.md | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/migrations.md b/migrations.md index 4a77db4775..0226b731b3 100644 --- a/migrations.md +++ b/migrations.md @@ -142,7 +142,7 @@ The `migrate:refresh` command will roll back all of your migrations and then exe // Refresh the database and run all database seeds... php artisan migrate:refresh --seed -You may roll back & re-migrate a limited number of migrations by providing the `step` option to the `refresh` command. For example, the following command will roll back & re-migrate the last five migrations: +You may roll back and re-migrate a limited number of migrations by providing the `step` option to the `refresh` command. For example, the following command will roll back and re-migrate the last five migrations: php artisan migrate:refresh --step=5 diff --git a/seeding.md b/seeding.md index 8587981a42..169b90af48 100644 --- a/seeding.md +++ b/seeding.md @@ -9,7 +9,9 @@ ## Introduction -Laravel includes a simple method of seeding your database with test data using seed classes. All seed classes are stored in the `database/seeders` directory. Seed classes may have any name you wish, but probably should follow some sensible convention, such as `UserSeeder`, etc. By default, a `DatabaseSeeder` class is defined for you. From this class, you may use the `call` method to run other seed classes, allowing you to control the seeding order. +Laravel includes the ability to seed your database with test data using seed classes. All seed classes are stored in the `database/seeders` directory. By default, a `DatabaseSeeder` class is defined for you. From this class, you may use the `call` method to run other seed classes, allowing you to control the seeding order. + +> {tip} [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled during database seeding. ## Writing Seeders @@ -20,8 +22,6 @@ To generate a seeder, execute the `make:seeder` [Artisan command](/docs/{{versio 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). -> {tip} [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled during database seeding. - As an example, let's modify the default `DatabaseSeeder` class and add a database insert statement to the `run` method: ### Calling Additional Seeders -Within the `DatabaseSeeder` class, you may use the `call` method to execute additional seed classes. Using the `call` method allows you to break up your database seeding into multiple files so that no single seeder class becomes overwhelmingly large. Pass the name of the seeder class you wish to run: +Within the `DatabaseSeeder` class, you may use the `call` method to execute additional seed classes. Using the `call` method allows you to break up your database seeding into multiple files so that no single seeder class becomes too large. The `call` method accepts an array of seeder classes that should be executed: /** * Run the database seeders. @@ -96,19 +96,19 @@ Within the `DatabaseSeeder` class, you may use the `call` method to execute addi ## Running Seeders -You may use the `db:seed` Artisan command to seed your database. By default, the `db:seed` command runs the `DatabaseSeeder` class, which may be used to call other seed classes. However, you may use the `--class` option to specify a specific seeder class to run individually: +You may execute the `db:seed` Artisan command to seed your database. By default, the `db:seed` command runs the `Database\Seeders\DatabaseSeeder` class, which may in turn invoke other seed classes. However, you may use the `--class` option to specify a specific seeder class to run individually: php artisan db:seed php artisan db:seed --class=UserSeeder -You may also seed your database using the `migrate:fresh` command, which will drop all tables and re-run all of your migrations. This command is useful for completely re-building your database: +You may also seed your database using the `migrate:fresh` command in combination with the `--seed` option, which will drop all tables and re-run all of your migrations. This command is useful for completely re-building your database: php artisan migrate:fresh --seed #### Forcing Seeders To Run In Production -Some seeding operations may cause you to alter or lose data. In order to protect you from running seeding commands against your production database, you will be prompted for confirmation before the seeders are executed. To force the seeders to run without a prompt, use the `--force` flag: +Some seeding operations may cause you to alter or lose data. In order to protect you from running seeding commands against your production database, you will be prompted for confirmation before the seeders are executed in the `production` environment. To force the seeders to run without a prompt, use the `--force` flag: php artisan db:seed --force From 5e5b2446c4bd8f1f6205fca68975fea7df2f0447 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 16:26:27 -0600 Subject: [PATCH 171/274] wip --- redis.md | 93 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/redis.md b/redis.md index 42f8d90023..5749008a1c 100644 --- a/redis.md +++ b/redis.md @@ -1,9 +1,10 @@ # Redis - [Introduction](#introduction) - - [Configuration](#configuration) +- [Configuration](#configuration) + - [Clusters](#clusters) - [Predis](#predis) - - [PhpRedis](#phpredis) + - [phpredis](#phpredis) - [Interacting With Redis](#interacting-with-redis) - [Pipelining Commands](#pipelining-commands) - [Pub / Sub](#pubsub) @@ -13,16 +14,18 @@ [Redis](https://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](https://redis.io/topics/data-types#strings), [hashes](https://redis.io/topics/data-types#hashes), [lists](https://redis.io/topics/data-types#lists), [sets](https://redis.io/topics/data-types#sets), and [sorted sets](https://redis.io/topics/data-types#sorted-sets). -Before using Redis with Laravel, we encourage you to install and use 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. +Before using Redis with Laravel, we encourage you to install and use the [phpredis](https://github.com/phpredis/phpredis) PHP extension via PECL. The extension is more complex to install compared to "user-land" PHP packages but may yield better performance for applications that make heavy use of Redis. If you are using [Laravel Sail](/docs/{{version}}/installation#laravel-sail), this extension is already installed in your application's Docker container. -Alternatively, you can install the `predis/predis` package via Composer: +If you are unable to install the phpredis extension, you may install the `predis/predis` package via Composer. Predis is a Redis client written entirely in PHP and does not require any additional extensions: - composer require predis/predis +```bash +composer require predis/predis +``` -### Configuration +## Configuration -The Redis configuration for your application is located in the `config/database.php` configuration file. Within this file, you will see a `redis` array containing the Redis servers utilized by your application: +You may configure your application's Redis settings via the `config/database.php` configuration file. Within this file, you will see a `redis` array containing the Redis servers utilized by your application: 'redis' => [ @@ -44,7 +47,7 @@ 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 unless you define a single URL to represent the Redis connection: +Each Redis server defined in your configuration file is required to have a name, host, and a port unless you define a single URL to represent the Redis connection: 'redis' => [ @@ -63,7 +66,7 @@ The default server configuration should suffice for development. However, you ar #### Configuring The Connection Scheme -By default, Redis clients will use the `tcp` scheme when connecting to your Redis servers; however, you may use TLS / SSL encryption by specifying a `scheme` configuration option in your Redis server configuration: +By default, Redis clients will use the `tcp` scheme when connecting to your Redis servers; however, you may use TLS / SSL encryption by specifying a `scheme` configuration option in your Redis server's configuration array: 'redis' => [ @@ -79,10 +82,10 @@ By default, Redis clients will use the `tcp` scheme when connecting to your Redi ], - -#### Configuring Clusters + +### Clusters -If your application is utilizing a cluster of Redis servers, you should define these clusters within a `clusters` key of your Redis configuration: +If your application is utilizing a cluster of Redis servers, you should define these clusters within a `clusters` key of your Redis configuration. This configuration key does not exist by default so you will need to create it within your application's `config/database.php` configuration file: 'redis' => [ @@ -101,7 +104,9 @@ If your application is utilizing a cluster of Redis servers, you should define t ], -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, it 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: +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, client-side sharding does not handle failover; therefore, it is primarily suited for transient cached data that is available from another primary data store. + +If you would like to use native Redis clustering instead of client-side sharding, you may specify this by setting the `options.cluster` configuration value to `redis` within your application's `config/database.php` configuration file: 'redis' => [ @@ -120,7 +125,7 @@ By default, clusters will perform client-side sharding across your nodes, allowi ### Predis -To utilize the Predis extension, you should change the `REDIS_CLIENT` environment variable from `phpredis` to `predis`: +If you would like your application to interact with Redis via the Predis package, you should ensure the `REDIS_CLIENT` environment variable's value is `predis`: 'redis' => [ @@ -129,7 +134,7 @@ To utilize the Predis extension, you should change the `REDIS_CLIENT` environmen // Rest of Redis configuration... ], -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, add them to your Redis server configuration in the `config/database.php` configuration file: +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, add them to your Redis server configuration in your application's `config/database.php` configuration file: 'default' => [ 'host' => env('REDIS_HOST', 'localhost'), @@ -139,10 +144,15 @@ In addition to the default `host`, `port`, `database`, and `password` server con 'read_write_timeout' => 60, ], + +#### The Redis Facade Alias + +Laravel's `config/app.php` configuration file contains an `aliases` array which defines all of the class aliases that will be registered by the framework. For convenience, an alias entry is included for each [facade](/docs/{{version}}/facades) offered by Laravel; however, the `Redis` alias is disabled because it conflicts with the `Redis` class name provided by the phpredis extension. If you are using the Predis client and would like to enable this alias, you may un-comment the alias in your application's `config/app.php` configuration file. + -### PhpRedis +### phpredis -The PhpRedis extension is configured as default at `REDIS_CLIENT` env and in your `config/database.php`: +By default, Laravel will use the phpredis extension to communicate with Redis. The client that Laravel will use to communicate with Redis is dictated by the value of the `redis.client` configuration option, which typically reflects the value of the `REDIS_CLIENT` environment variable: 'redis' => [ @@ -151,11 +161,7 @@ The PhpRedis extension is configured as default at `REDIS_CLIENT` env and in you // Rest of Redis configuration... ], -If you plan to use PhpRedis extension along with the `Redis` Facade alias, you should rename it to something else, like `RedisManager`, to avoid a collision with the Redis class. You can do that in the aliases section of your `app.php` config file. - - 'RedisManager' => Illuminate\Support\Facades\Redis::class, - -In addition to the default `host`, `port`, `database`, and `password` server configuration options, PhpRedis supports the following additional connection parameters: `persistent`, `prefix`, `read_timeout`, `retry_interval`, `timeout`, and `context`. You may add any of these options to your Redis server configuration in the `config/database.php` configuration file: +In addition to the default `host`, `port`, `database`, and `password` server configuration options, phpredis supports the following additional connection parameters: `persistent`, `prefix`, `read_timeout`, `retry_interval`, `timeout`, and `context`. 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'), @@ -169,11 +175,6 @@ In addition to the default `host`, `port`, `database`, and `password` server con ], ], - -#### The Redis Facade - -To avoid class naming collisions with the Redis PHP extension itself, you will need to delete or rename the `Illuminate\Support\Facades\Redis` facade alias from your `app` configuration file's `aliases` array. Generally, you should remove this alias entirely and only reference the facade by its fully qualified class name while using the Redis PHP extension. - ## Interacting With Redis @@ -192,41 +193,43 @@ You may interact with Redis by calling various methods on the `Redis` [facade](/ * Show the profile for the given user. * * @param int $id - * @return Response + * @return \Illuminate\Http\Response */ - public function showProfile($id) + public function show($id) { - $user = Redis::get('user:profile:'.$id); - - return view('user.profile', ['user' => $user]); + return view('user.profile', [ + 'user' => Redis::get('user:profile:'.$id) + ]); } } -As mentioned above, you may call any of the Redis commands on the `Redis` facade. Laravel uses magic methods to pass the commands to the Redis server, so pass the arguments the Redis command expects: +As mentioned above, you may call any of Redis' commands on the `Redis` facade. Laravel uses magic methods to pass the commands to the Redis server. If a Redis command expects arguments, you should pass those to the facade's corresponding method: + + use Illuminate\Support\Facades\Redis; Redis::set('name', 'Taylor'); $values = Redis::lrange('names', 5, 10); -Alternatively, you may also pass commands to the server using the `command` method, which accepts the name of the command as its first argument, and an array of values as its second argument: +Alternatively, you may pass commands to the server using the `Redis` facade's `command` method, which accepts the name of the command as its first argument and an array of values as its second argument: $values = Redis::command('lrange', ['name', 5, 10]); #### Using Multiple Redis Connections -You may get a Redis instance by calling the `Redis::connection` method: +Your application's `config/database.php` configuration file allows you to define multiple Redis connections / servers. You may obtain a connection to a specific Redis connection using the `Redis` facade's `connection` method: - $redis = Redis::connection(); + $redis = Redis::connection('connection-name'); -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: +To obtain an instance of the default Redis connection, you may call the `connection` method without any additional arguments: - $redis = Redis::connection('my-connection'); + $redis = Redis::connection(); ### Pipelining Commands -Pipelining should be used when you need to send many commands to the server. The `pipeline` method accepts one argument: a closure that receives a Redis instance. You may issue all of your commands to this Redis instance and they will all be streamed to the server thus providing better performance: +Sometimes you may need to execute dozens of Redis commands. Instead of making a network trip to your Redis server for each command, you may use the `pipeline` method. The `pipeline` method accepts one argument: a closure that receives a Redis instance. You may issue all of your commands to this Redis instance and they will all be sent to the Redis server at the same time to reduce network trips to the server. The commands will still be executed in the order they were issued: Redis::pipeline(function ($pipe) { for ($i = 0; $i < 1000; $i++) { @@ -279,16 +282,20 @@ First, let's setup a channel listener using the `subscribe` method. We'll place Now we may publish messages to the channel using the `publish` method: - Route::get('publish', function () { - // Route logic... + use Illuminate\Support\Facades\Redis; + + Route::get('/publish', function () { + // ... - Redis::publish('test-channel', json_encode(['foo' => 'bar'])); + Redis::publish('test-channel', json_encode([ + 'name' => 'Adam Wathan' + ])); }); #### Wildcard Subscriptions -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: +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 closure: Redis::psubscribe(['*'], function ($message, $channel) { echo $message; From 380caee3f492126834af0748f4804cec7bfa8c41 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 24 Nov 2020 16:51:42 -0600 Subject: [PATCH 172/274] document lua scripting --- redis.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/redis.md b/redis.md index 5749008a1c..c217e2f6ca 100644 --- a/redis.md +++ b/redis.md @@ -6,6 +6,7 @@ - [Predis](#predis) - [phpredis](#phpredis) - [Interacting With Redis](#interacting-with-redis) + - [Transactions](#transactions) - [Pipelining Commands](#pipelining-commands) - [Pub / Sub](#pubsub) @@ -226,11 +227,47 @@ To obtain an instance of the default Redis connection, you may call the `connect $redis = Redis::connection(); + +### Transactions + +The `Redis` facade's `transaction` method provides a convenient wrapper around Redis' native `MULTI` and `EXEC` commands. The `transaction` method accepts a closure as its only argument. This closure will receive a Redis connection instance and may issue any commands it would like to this instance. All of the Redis commands issued within the closure will be executed in a single, atomic transaction: + + use Illuminate\Support\Facades\Redis; + + Redis::transaction(function ($redis) { + $redis->incr('user_visits', 1); + $redis->incr('total_visits', 1); + }); + +> {note} When defining a Redis transaction, you may not retrieve any values from the Redis connection. Remember, your transaction is executed as a single, atomic operation and that operation is not executed until your entire closure has finished executing its commands. + +#### Lua Scripts + +The `eval` method provides another method of executing multiple Redis commands in a single, atomic operation. However, the `eval` method has the benefit of being able to interact with and inspect Redis key values during that operation. Redis scripts are written in the [Lua programming language](https://www.lua.org). + +The `eval` method can be a bit scary at first, but we'll explore a basic example to break the ice. The `eval` method expects several arguments. First, you should pass the Lua script (as a string) to the method. Secondly, you should pass the number of keys (as an integer) that the script interacts with. Thirdly, you should pass the names of those keys. Finally, you may pass any other additional arguments that you need to access within your script. + +In this example, we will increment a counter, inspect its new value, and increment a second counter if the first counter's value is greater than five. Finally, we will return the value of the first counter: + + $value = Redis::eval(<<<'LUA' + local counter = redis.call("incr", KEYS[1]) + + if counter > 5 then + redis.call("incr", KEYS[2]) + end + + return counter + LUA, 2, 'first-counter', 'second-counter'); + +> {note} Please consult the [Redis documentation](https://redis.io/commands/eval) for more information on Redis scripting. + ### Pipelining Commands Sometimes you may need to execute dozens of Redis commands. Instead of making a network trip to your Redis server for each command, you may use the `pipeline` method. The `pipeline` method accepts one argument: a closure that receives a Redis instance. You may issue all of your commands to this Redis instance and they will all be sent to the Redis server at the same time to reduce network trips to the server. The commands will still be executed in the order they were issued: + use Illuminate\Support\Facades\Redis; + Redis::pipeline(function ($pipe) { for ($i = 0; $i < 1000; $i++) { $pipe->set("key:$i", $i); From 728cd401d3bc5e19a6f5c7c75572f28051212509 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 13:07:13 -0600 Subject: [PATCH 173/274] wip --- eloquent.md | 130 ++++++++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 59 deletions(-) diff --git a/eloquent.md b/eloquent.md index ff313bbe48..01890a1e23 100644 --- a/eloquent.md +++ b/eloquent.md @@ -1,8 +1,12 @@ # Eloquent: Getting Started - [Introduction](#introduction) -- [Defining Models](#defining-models) - - [Eloquent Model Conventions](#eloquent-model-conventions) +- [Generating Model Classes](#generating-model-classes) +- [Eloquent Model Conventions](#eloquent-model-conventions) + - [Table Names](#table-names) + - [Primary Keys](#primary-keys) + - [Timestamps](#timestamps) + - [Database Connections](#database-connections) - [Default Attribute Values](#default-attribute-values) - [Retrieving Models](#retrieving-models) - [Collections](#collections) @@ -31,16 +35,14 @@ ## Introduction -The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table. +Laravel includes Eloquent, an object-relational mapper (ORM) that makes it enjoyable to interact with your database. When using Eloquent, each database table has a corresponding "Model" that is used to interact with that table. In addition to retrieving records from the database table, Eloquent models allow you to insert, update, and delete records from the table as well. -Before getting started, be sure to configure a database connection in `config/database.php`. For more information on configuring your database, check out [the documentation](/docs/{{version}}/database#configuration). +> {tip} Before getting started, be sure to configure a database connection in your application's `config/database.php` configuration file. For more information on configuring your database, check out [the database configuration documentation](/docs/{{version}}/database#configuration). - -## Defining Models + +## Generating Model Classes -To get started, let's create an Eloquent model. Models typically live in the `app\Models` directory, but you are free to place them anywhere that can be auto-loaded according to your `composer.json` file. All Eloquent models extend `Illuminate\Database\Eloquent\Model` class. - -The easiest way to create a model instance is using the `make:model` [Artisan command](/docs/{{version}}/artisan): +To get started, let's create an Eloquent model. Models typically live in the `app\Models` directory and extend the `Illuminate\Database\Eloquent\Model` class. You may use the `make:model` [Artisan command](/docs/{{version}}/artisan) to generate a new model: php artisan make:model Flight @@ -48,25 +50,29 @@ If you would like to generate a [database migration](/docs/{{version}}/migration php artisan make:model Flight --migration - php artisan make:model Flight -m - You may generate various other types of classes when generating a model, such as factories, seeders, and controllers. In addition, these options may be combined to create multiple classes at once: - php artisan make:model Flight --factory - php artisan make:model Flight -f +```bash +# Generate a model and a FlightFactory class... +php artisan make:model Flight --factory +php artisan make:model Flight -f - php artisan make:model Flight --seed - php artisan make:model Flight -s +# Generate a model and a FlightSeeder class... +php artisan make:model Flight --seed +php artisan make:model Flight -s - php artisan make:model Flight --controller - php artisan make:model Flight -c +# Generate a model and a FlightController class... +php artisan make:model Flight --controller +php artisan make:model Flight -c - php artisan make:model Flight -mfsc +# Generate a model and a migration, factory, seeder, and controller... +php artisan make:model Flight -mfsc +``` -### Eloquent Model Conventions +## Eloquent Model Conventions -Now, let's look at an example `Flight` model, which we will use to retrieve and store information from our `flights` database table: +Models generated by the `make:model` command will be placed in the `app/Models` directory. Let's examine a basic model class and discuss some of Eloquent's key conventions: -#### Table Names +### Table Names -Note that we did not tell Eloquent which table to use for our `Flight` model. By convention, the "snake case", plural name of the class will be used as the table name unless another name is explicitly specified. So, in this case, Eloquent will assume the `Flight` model stores records in the `flights` table, while an `AirTrafficController` model would store records in an `air_traffic_controllers` table. +After glancing at the example above, you may have noticed that we did not tell Eloquent which database table corresponds to our `Flight` model. By convention, the "snake case", plural name of the class will be used as the table name unless another name is explicitly specified. So, in this case, Eloquent will assume the `Flight` model stores records in the `flights` table, while an `AirTrafficController` model would store records in an `air_traffic_controllers` table. -You can manually specify a table name by defining a `table` property on your model: +If your model's corresponding database table does not fit this convention, you may manually specify the model's table name by defining a `table` property on the model: -#### Primary Keys +### Primary Keys -Eloquent will also assume that each table has a primary key column named `id`. You may define a protected `$primaryKey` property to override this convention: +Eloquent will also assume that each model's corresponding database table has a primary key column named `id`. If necessary, you may define a protected `$primaryKey` property on your model to specify a different column that serves as your model's primary key: -#### Timestamps +### Timestamps -By default, Eloquent expects `created_at` and `updated_at` columns to exist on your tables. If you do not wish to have these columns automatically managed by Eloquent, set the `$timestamps` property on your model to `false`: +By default, Eloquent expects `created_at` and `updated_at` columns to exist on your model's corresponding database table. Eloquent will automatically set these column's values when models are created or updated. If you do not want these columns to be automatically managed by Eloquent, you should define a `$timestamps` property on your model with a value of `false`: -#### Database Connection + +### Database Connections -By default, all Eloquent models will use the default database connection configured for your application. If you would like to specify a different connection for the model, use the `$connection` property: +By default, all Eloquent models will use the default database connection that is configured for your application. If you would like to specify a different connection that should be used when interacting with a particular model, you should define a `$connection` property on the model: ### Default Attribute Values -If you would like to define the default values for some of your model's attributes, you may define an `$attributes` property on your model: +By default, a newly instantiated model instance will not contain any attribute values. If you would like to define the default values for some of your model's attributes, you may define an `$attributes` property on your model: ## Retrieving Models -Once you have created a model and [its associated database table](/docs/{{version}}/migrations#writing-migrations), you are ready to start retrieving data from your database. Think of each Eloquent model as a powerful [query builder](/docs/{{version}}/queries) allowing you to fluently query the database table associated with the model. For example: +Once you have created a model and [its associated database table](/docs/{{version}}/migrations#writing-migrations), you are ready to start retrieving data from your database. You can think of each Eloquent model as a powerful [query builder](/docs/{{version}}/queries) allowing you to fluently query the database table associated with the model. The model's `all` method will retrieve all of the records from the model's associated database table: - name; } - -#### Adding Additional Constraints + +#### Building Queries -The Eloquent `all` method will return all of the results in the model's table. Since each Eloquent model serves as a [query builder](/docs/{{version}}/queries), you may also add constraints to queries, and then use the `get` method to retrieve the results: +The Eloquent `all` method will return all of the results in the model's table. However, since each Eloquent model serves as a [query builder](/docs/{{version}}/queries), you may add additional constraints to queries and then invoke the `get` method to retrieve the results: - $flights = App\Models\Flight::where('active', 1) - ->orderBy('name', 'desc') + $flights = Flight::where('active', 1) + ->orderBy('name') ->take(10) ->get(); -> {tip} Since Eloquent models are query builders, you should review all of the methods available on the [query builder](/docs/{{version}}/queries). You may use any of these methods in your Eloquent queries. +> {tip} Since Eloquent models are query builders, you should review all of the methods provided by Laravel's [query builder](/docs/{{version}}/queries). You may use any of these methods when writing your Eloquent queries. #### Refreshing Models -You can refresh models using the `fresh` and `refresh` methods. The `fresh` method will re-retrieve the model from the database. The existing model instance will not be affected: +If you already have an instance of an Eloquent model that was retrieved from the database, you can "refresh" the model using the `fresh` and `refresh` methods. The `fresh` method will re-retrieve the model from the database. The existing model instance will not be affected: - $flight = App\Models\Flight::where('number', 'FR 900')->first(); + $flight = Flight::where('number', 'FR 900')->first(); $freshFlight = $flight->fresh(); The `refresh` method will re-hydrate the existing model using fresh data from the database. In addition, all of its loaded relationships will be refreshed as well: - $flight = App\Models\Flight::where('number', 'FR 900')->first(); + $flight = Flight::where('number', 'FR 900')->first(); $flight->number = 'FR 456'; @@ -291,13 +295,21 @@ The `refresh` method will re-hydrate the existing model using fresh data from th ### Collections -For Eloquent methods like `all` and `get` which retrieve multiple results, an instance of `Illuminate\Database\Eloquent\Collection` will be returned. The `Collection` class provides [a variety of helpful methods](/docs/{{version}}/eloquent-collections#available-methods) for working with your Eloquent results: +As we have seen, Eloquent methods like `all` and `get` retrieve multiple records from the database. However, these methods do return a plain PHP array. Instead, an instance of `Illuminate\Database\Eloquent\Collection` is returned. - $flights = $flights->reject(function ($flight) { - return $flight->cancelled; - }); +The Eloquent `Collection` class extends Laravel's base `Illuminate\Support\Collection` class, which provides a [variety of helpful methods](/docs/{{version}}/collections#available-methods) for interacting with data collections. For example, the `reject` method may be used to remove models from a collection based on the results of an invoked closure: + +```php +$flights = Flight::where('destination', 'Paris')->get(); + +$flights = $flights->reject(function ($flight) { + return $flight->cancelled; +}); +``` + +In addition to the methods provided by Laravel's base collection class, the Eloquent collection class provides [a few extra methods](/docs/{{version}}/eloquent-collections#available-methods) that are specifically intended for interacting with collections of Eloquent models. -You may also loop over the collection like an array: +Since all of Laravel's collections implement PHP's iterable interfaces, you may loop over collections as if they were an array: foreach ($flights as $flight) { echo $flight->name; From 4131c721ba40fa43c95cd95edf632c32dc08c178 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 13:33:01 -0600 Subject: [PATCH 174/274] wip --- eloquent.md | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/eloquent.md b/eloquent.md index 01890a1e23..0a1bfc9d11 100644 --- a/eloquent.md +++ b/eloquent.md @@ -11,6 +11,7 @@ - [Retrieving Models](#retrieving-models) - [Collections](#collections) - [Chunking Results](#chunking-results) + - [Cursors](#cursors) - [Advanced Subqueries](#advanced-subqueries) - [Retrieving Single Models / Aggregates](#retrieving-single-models) - [Retrieving Aggregates](#retrieving-aggregates) @@ -318,7 +319,11 @@ Since all of Laravel's collections implement PHP's iterable interfaces, you may ### Chunking Results -If you need to process thousands of Eloquent records, use the `chunk` command. The `chunk` method will retrieve a "chunk" of Eloquent models, feeding them to a given closure for processing. Using the `chunk` method will conserve memory when working with large result sets: +Your application may run out of memory if you attempt to load tens of thousands of Eloquent records via the `all` or `get` methods. Instead of using these methods, the `chunk` method may be used to process large numbers of models more efficiently. + +The `chunk` method will retrieve a subset of Eloquent models, passing them to a closure for processing. Since only the current chunk of Eloquent models is retrieved at a time, the `chunk` method will provide significantly reduced memory usage when working with a large amount of models: + + use App\Models\Flight; Flight::chunk(200, function ($flights) { foreach ($flights as $flight) { @@ -326,26 +331,33 @@ If you need to process thousands of Eloquent records, use the `chunk` command. T } }); -The first argument passed to the method is the number of records you wish to receive per "chunk". The closure passed as the second argument will be called for each chunk that is retrieved from the database. A database query will be executed to retrieve each chunk of records passed to the closure. +The first argument passed to the `chunk` method is the number of records you wish to receive per "chunk". The closure passed as the second argument will be invoked for each chunk that is retrieved from the database. A database query will be executed to retrieve each chunk of records passed to the closure. -If you are filtering the results of the `chunk` method based on a column that you will also be updating while iterating over the results, you should use the `chunkById` method. Using the `chunk` method in these scenarios could lead to unexpected and inconsistent results: +If you are filtering the results of the `chunk` method based on a column that you will also be updating while iterating over the results, you should use the `chunkById` method. Using the `chunk` method in these scenarios could lead to unexpected and inconsistent results. Internally, the `chunkById` method will always retrieve models with an `id` column greater than the last model in the previous chunk: - Flight::where('departed', true)->chunkById(200, function ($flights) { - $flights->each->update(['departed' => false]); - }); + Flight::where('departed', true) + ->chunkById(200, function ($flights) { + $flights->each->update(['departed' => false]); + }, $column = 'id'); + + +### Cursors - -#### Using Cursors +Similar to the `chunk` method, the `cursor` method may be used to significantly reduce your application's memory consumption when iterating through tens of thousands of Eloquent model records. -The `cursor` method allows you to iterate through your database records using a cursor, which will only execute a single query. When processing large amounts of data, the `cursor` method may be used to greatly reduce your memory usage: +The `cursor` method will only execute a single database query; however, the individual Eloquent models will not be hydrated until they are actually iterated over. Therefore, only one Eloquent model is kept in memory at any given time while iterating over the cursor. Internally, the `cursor` method uses PHP [generators](https://www.php.net/manual/en/language.generators.overview.php) to implement this functionality: - foreach (Flight::where('foo', 'bar')->cursor() as $flight) { + use App\Models\Flight; + + foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) { // } The `cursor` returns an `Illuminate\Support\LazyCollection` instance. [Lazy collections](/docs/{{version}}/collections#lazy-collections) allow you to use many of the collection methods available on typical Laravel collections while only loading a single model into memory at a time: - $users = App\Models\User::cursor()->filter(function ($user) { + use App\Models\User; + + $users = User::cursor()->filter(function ($user) { return $user->id > 500; }); From 68489710384c5fae9f080e3d8b864763ffc8c886 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 13:35:51 -0600 Subject: [PATCH 175/274] wip --- eloquent.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/eloquent.md b/eloquent.md index 0a1bfc9d11..642d22aef4 100644 --- a/eloquent.md +++ b/eloquent.md @@ -373,26 +373,26 @@ The `cursor` returns an `Illuminate\Support\LazyCollection` instance. [Lazy coll Eloquent also offers advanced subquery support, which allows you to pull information from related tables in a single query. For example, let's imagine that we have a table of flight `destinations` and a table of `flights` to destinations. The `flights` table contains an `arrived_at` column which indicates when the flight arrived at the destination. -Using the subquery functionality available to the `select` and `addSelect` methods, we can select all of the `destinations` and the name of the flight that most recently arrived at that destination using a single query: +Using the subquery functionality available to the query builder's `select` and `addSelect` methods, we can select all of the `destinations` and the name of the flight that most recently arrived at that destination using a single query: use App\Models\Destination; use App\Models\Flight; return Destination::addSelect(['last_flight' => Flight::select('name') ->whereColumn('destination_id', 'destinations.id') - ->orderBy('arrived_at', 'desc') + ->orderByDesc('arrived_at') ->limit(1) ])->get(); #### Subquery Ordering -In addition, the query builder's `orderBy` function supports subqueries. We may use this functionality to sort all destinations based on when the last flight arrived at that destination. Again, this may be done while executing a single query against the database: +In addition, the query builder's `orderBy` function supports subqueries. Continuing to use our flight example, we may use this functionality to sort all destinations based on when the last flight arrived at that destination. Again, this may be done while executing a single database query: return Destination::orderByDesc( Flight::select('arrived_at') ->whereColumn('destination_id', 'destinations.id') - ->orderBy('arrived_at', 'desc') + ->orderByDesc('arrived_at') ->limit(1) )->get(); From c226925cb6e73e6d2289c8c8ff9e1a17633fff82 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 13:47:13 -0600 Subject: [PATCH 176/274] wip --- eloquent.md | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/eloquent.md b/eloquent.md index 642d22aef4..491dddefaa 100644 --- a/eloquent.md +++ b/eloquent.md @@ -399,57 +399,50 @@ In addition, the query builder's `orderBy` function supports subqueries. Continu ## Retrieving Single Models / Aggregates -In addition to retrieving all of the records for a given table, you may also retrieve single records using `find`, `first`, or `firstWhere`. Instead of returning a collection of models, these methods return a single model instance: +In addition to retrieving all of the records matching a given query, you may also retrieve single records using the `find`, `first`, or `firstWhere` methods. Instead of returning a collection of models, these methods return a single model instance: + + use App\Models\Flight; // Retrieve a model by its primary key... - $flight = App\Models\Flight::find(1); + $flight = Flight::find(1); // Retrieve the first model matching the query constraints... - $flight = App\Models\Flight::where('active', 1)->first(); - - // Shorthand for retrieving the first model matching the query constraints... - $flight = App\Models\Flight::firstWhere('active', 1); - -You may also call the `find` method with an array of primary keys, which will return a collection of the matching records: + $flight = Flight::where('active', 1)->first(); - $flights = App\Models\Flight::find([1, 2, 3]); + // Alternative to retrieving the first model matching the query constraints... + $flight = Flight::firstWhere('active', 1); -Sometimes you may wish to retrieve the first result of a query or perform some other action if no results are found. The `firstOr` method will return the first result that is found or, if no results are found, execute the given callback. The result of the callback will be considered the result of the `firstOr` method: +Sometimes you may wish to retrieve the first result of a query or perform some other action if no results are found. The `firstOr` method will return the first result matching the query or, if no results are found, execute the given closure. The value returned by the closure will be considered the result of the `firstOr` method: - $model = App\Models\Flight::where('legs', '>', 100)->firstOr(function () { - // ... + $model = Flight::where('legs', '>', 3)->firstOr(function () { + // ... }); -The `firstOr` method also accepts an array of columns to retrieve: - - $model = App\Models\Flight::where('legs', '>', 100) - ->firstOr(['id', 'legs'], function () { - // ... - }); - #### Not Found Exceptions Sometimes you may wish to throw an exception if a model is not found. This is particularly useful in routes or controllers. The `findOrFail` and `firstOrFail` methods will retrieve the first result of the query; however, if no result is found, an `Illuminate\Database\Eloquent\ModelNotFoundException` will be thrown: - $model = App\Models\Flight::findOrFail(1); + $flight = Flight::findOrFail(1); - $model = App\Models\Flight::where('legs', '>', 100)->firstOrFail(); + $flight = Flight::where('legs', '>', 3)->firstOrFail(); -If the exception is not caught, a `404` HTTP response is automatically sent back to the user. It is not necessary to write explicit checks to return `404` responses when using these methods: +If the `ModelNotFoundException` is not caught, a 404 HTTP response is automatically sent back to the client: + + use App\Models\Flight; Route::get('/api/flights/{id}', function ($id) { - return App\Models\Flight::findOrFail($id); + return Flight::findOrFail($id); }); ### Retrieving Aggregates -You may also use the `count`, `sum`, `max`, and other [aggregate methods](/docs/{{version}}/queries#aggregates) provided by the [query builder](/docs/{{version}}/queries). These methods return the appropriate scalar value instead of a full model instance: +When interacting with Eloquent models, you may also use the `count`, `sum`, `max`, and other [aggregate methods](/docs/{{version}}/queries#aggregates) provided by the Laravel [query builder](/docs/{{version}}/queries). As you might expect, these methods return a scalar value instead of an Eloquent model instance: - $count = App\Models\Flight::where('active', 1)->count(); + $count = Flight::where('active', 1)->count(); - $max = App\Models\Flight::where('active', 1)->max('price'); + $max = Flight::where('active', 1)->max('price'); ## Inserting & Updating Models From e585992c29aacc62bf2488030a66eddfdac743eb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 14:04:08 -0600 Subject: [PATCH 177/274] wip --- eloquent.md | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/eloquent.md b/eloquent.md index 491dddefaa..ea04a4f003 100644 --- a/eloquent.md +++ b/eloquent.md @@ -450,7 +450,7 @@ When interacting with Eloquent models, you may also use the `count`, `sum`, `max ### Inserts -To create a new record in the database, create a new model instance, set attributes on the model, then call the `save` method: +Of course, when using Eloquent, we don't only need to retrieve models from the database. We also need to insert new records. Thankfully, Eloquent makes it simple. To insert a new record into the database, you should instantiate a new model instance and set attributes on the model. Then, call the `save` method on the model instance: ### Updates -The `save` method may also be used to update models that already exist in the database. To update a model, you should retrieve it, set any attributes you wish to update, and then call the `save` method. Again, the `updated_at` timestamp will automatically be updated, so there is no need to manually set its value: +The `save` method may also be used to update models that already exist in the database. To update a model, you should retrieve it and set any attributes you wish to update. Then, you should call the model's `save` method. Again, the `updated_at` timestamp will automatically be updated, so there is no need to manually set its value: - $flight = App\Models\Flight::find(1); + use App\Models\Flight; + + $flight = Flight::find(1); - $flight->name = 'New Flight Name'; + $flight->name = 'Paris to London'; $flight->save(); #### Mass Updates -Updates can also be performed against any number of models that match a given query. In this example, all flights that are `active` and have a `destination` of `San Diego` will be marked as delayed: +Updates can also be performed against models that match a given query. In this example, all flights that are `active` and have a `destination` of `San Diego` will be marked as delayed: - App\Models\Flight::where('active', 1) - ->where('destination', 'San Diego') - ->update(['delayed' => 1]); + Flight::where('active', 1) + ->where('destination', 'San Diego') + ->update(['delayed' => 1]); The `update` method expects an array of column and value pairs representing the columns that should be updated. @@ -509,9 +511,11 @@ The `update` method expects an array of column and value pairs representing the #### Examining Attribute Changes -Eloquent provides the `isDirty`, `isClean`, and `wasChanged` methods to examine the internal state of your model and determine how its attributes have changed from when they were originally loaded. +Eloquent provides the `isDirty`, `isClean`, and `wasChanged` methods to examine the internal state of your model and determine how its attributes have changed from when the model was originally retrieved. -The `isDirty` method determines if any attributes have been changed since the model was loaded. You may pass a specific attribute name to determine if a particular attribute is dirty. The `isClean` method is the opposite of `isDirty` and also accepts an optional attribute argument: +The `isDirty` method determines if any of the model's attributes have been changed since the model was retrieved. You may pass a specific attribute name to the `isDirty` method to determine if a particular attribute is dirty. The `isClean` will determine if an attribute has remained unchanged since the model was retrieved. This method also accepts an optional attribute argument: + + use App\Models\User; $user = User::create([ 'first_name' => 'Taylor', @@ -534,7 +538,7 @@ The `isDirty` method determines if any attributes have been changed since the mo $user->isDirty(); // false $user->isClean(); // true -The `wasChanged` method determines if any attributes were changed when the model was last saved within the current request cycle. You may also pass an attribute name to see if a particular attribute was changed: +The `wasChanged` method determines if any attributes were changed when the model was last saved within the current request cycle. If needed, you may pass an attribute name to see if a particular attribute was changed: $user = User::create([ 'first_name' => 'Taylor', @@ -543,13 +547,14 @@ The `wasChanged` method determines if any attributes were changed when the model ]); $user->title = 'Painter'; + $user->save(); $user->wasChanged(); // true $user->wasChanged('title'); // true $user->wasChanged('first_name'); // false -The `getOriginal` method returns an array containing the original attributes of the model regardless of any changes since the model was loaded. You may pass a specific attribute name to get the original value of a particular attribute: +The `getOriginal` method returns an array containing the original attributes of the model regardless of any changes to the model since it was retrieved. If needed, you may pass a specific attribute name to get the original value of a particular attribute: $user = User::find(1); From 8cc7a9590ecd137636a756903fa32163da76f53d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 14:46:58 -0600 Subject: [PATCH 178/274] wip --- eloquent.md | 223 +++++++++++++++++++++++++++------------------------- 1 file changed, 118 insertions(+), 105 deletions(-) diff --git a/eloquent.md b/eloquent.md index ea04a4f003..3c8f59511f 100644 --- a/eloquent.md +++ b/eloquent.md @@ -14,12 +14,13 @@ - [Cursors](#cursors) - [Advanced Subqueries](#advanced-subqueries) - [Retrieving Single Models / Aggregates](#retrieving-single-models) + - [Retrieving Or Creating Models](#retrieving-or-creating-models) - [Retrieving Aggregates](#retrieving-aggregates) - [Inserting & Updating Models](#inserting-and-updating-models) - [Inserts](#inserts) - [Updates](#updates) - [Mass Assignment](#mass-assignment) - - [Other Creation Methods](#other-creation-methods) + - [Upserts](#upserts) - [Deleting Models](#deleting-models) - [Soft Deleting](#soft-deleting) - [Querying Soft Deleted Models](#querying-soft-deleted-models) @@ -435,6 +436,37 @@ If the `ModelNotFoundException` is not caught, a 404 HTTP response is automatica return Flight::findOrFail($id); }); + +### Retrieving Or Creating Models + +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 attributes resulting from merging the first array argument with the optional second array argument: + +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 manually call the `save` method to persist it: + + use App\Models\Flight; + + // Retrieve flight by name or create it if it doesn't exist... + $flight = Flight::firstOrCreate([ + 'name' => 'London to Paris' + ]); + + // Retrieve flight by name or create it with the name, delayed, and arrival_time attributes... + $flight = Flight::firstOrCreate( + ['name' => 'London to Paris'], + ['delayed' => 1, 'arrival_time' => '11:30'] + ); + + // Retrieve flight by name or instantiate a new Flight instance... + $flight = Flight::firstOrNew([ + 'name' => 'London to Paris' + ]); + + // Retrieve flight by name or instantiate with the name, delayed, and arrival_time attributes... + $flight = Flight::firstOrNew( + ['name' => 'Tokyo to Sydney'], + ['delayed' => 1, 'arrival_time' => '11:30'] + ); + ### Retrieving Aggregates @@ -570,9 +602,17 @@ The `getOriginal` method returns an array containing the original attributes of ### Mass Assignment -You may also use the `create` method to save a new model in a single line. The inserted model instance will be returned to you from the method. However, before doing so, you will need to specify either a `fillable` or `guarded` attribute on the model, as all Eloquent models protect against mass-assignment by default. +You may use the `create` method to save a new model using a single PHP statement. The inserted model instance will be returned to you from the method: -A mass-assignment vulnerability occurs when a user passes an unexpected HTTP parameter through a request, and that parameter changes a column in your database you did not expect. For example, a malicious user might send an `is_admin` parameter through an HTTP request, which is then passed into your model's `create` method, allowing the user to escalate themselves to an administrator. + use App\Models\Flight; + + $flight = Flight::create([ + 'name' => 'London to Paris', + ]); + +However, before using the `create` method, you will need to specify either a `fillable` or `guarded` property on your model class. These properties are required because all Eloquent models are protected against mass assignment vulnerabilities by default. + +A mass assignment vulnerability occurs when a user passes an unexpected HTTP request field and that field changes a column in your database that you did not expect. For example, a malicious user might send an `is_admin` parameter through an HTTP request, which is then passed to your model's `create` method, allowing the user to escalate themselves to an administrator. So, to get started, you should define which model attributes you want to make mass assignable. You may do this using the `$fillable` property on the model. For example, let's make the `name` attribute of our `Flight` model mass assignable: @@ -592,18 +632,18 @@ So, to get started, you should define which model attributes you want to make ma protected $fillable = ['name']; } -Once we have made the attributes mass assignable, we can use the `create` method to insert a new record in the database. The `create` method returns the saved model instance: +Once you have specified which attributes are mass assignable, you may use the `create` method to insert a new record in the database. The `create` method returns the newly created model instance: - $flight = App\Models\Flight::create(['name' => 'Flight 10']); + $flight = Flight::create(['name' => 'London to Paris']); 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']); + $flight->fill(['name' => 'Amsterdam to Frankfurt']); #### Mass Assignment & JSON Columns -When assigning JSON columns, each column's mass-assignable key must be specified in your model's `$fillable` array. For security, Laravel does not support updating nested JSON attributes when using the `guarded` property: +When assigning JSON columns, each column's mass assignable key must be specified in your model's `$fillable` array. For security, Laravel does not support updating nested JSON attributes when using the `guarded` property: /** * The attributes that are mass assignable. @@ -617,7 +657,7 @@ When assigning JSON columns, each column's mass-assignable key must be specified #### Allowing Mass Assignment -If you would like to make all attributes mass assignable, you may define the `$guarded` property as an empty array: +If you would like to make all of your attributes mass assignable, you may define your model's `$guarded` property as an empty array. If you choose to unguard your model, you should take special care to always hand-craft the arrays passed to Eloquent's `fill`, `create`, and `update` methods: /** * The attributes that aren't mass assignable. @@ -626,92 +666,66 @@ If you would like to make all attributes mass assignable, you may define the `$g */ protected $guarded = []; - -### 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 attributes from the first parameter, along with those in the optional second parameter. + +### Upserts -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: +Occasionally, you may need to update an existing model or create a new model if no matching model exists. Like the `firstOrCreate` method, the `updateOrCreate` method persists the model, so there's no need to manually call the `save` method. - // Retrieve flight by name, or create it if it doesn't exist... - $flight = App\Models\Flight::firstOrCreate(['name' => 'Flight 10']); +In the example below, if a flight exists with a `departure` location of `Oakland` and a `destination` location of `San Diego`, it's `price` and `discounted` columns will be updated. If no such flight exists, a new flight will be created which has the attributes resulting from merging the first argument array with the second argument array: - // Retrieve flight by name, or create it with the name, delayed, and arrival_time attributes... - $flight = App\Models\Flight::firstOrCreate( - ['name' => 'Flight 10'], - ['delayed' => 1, 'arrival_time' => '11:30'] - ); - - // Retrieve by name, or instantiate... - $flight = App\Models\Flight::firstOrNew(['name' => 'Flight 10']); - - // Retrieve by name, or instantiate with the name, delayed, and arrival_time attributes... - $flight = App\Models\Flight::firstOrNew( - ['name' => 'Flight 10'], - ['delayed' => 1, 'arrival_time' => '11:30'] - ); - - -#### `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\Models\Flight::updateOrCreate( + $flight = Flight::updateOrCreate( ['departure' => 'Oakland', 'destination' => 'San Diego'], ['price' => 99, 'discounted' => 1] ); If you would like to perform multiple "upserts" in a single query, then you should use the `upsert` method instead. The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table. The method's third and final argument is an array of the columns that should be updated if a matching record already exists in the database. The `upsert` method will automatically set the `created_at` and `updated_at` timestamps if timestamps are enabled on the model: - App\Models\Flight::upsert([ + Flight::upsert([ ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99], ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150] ], ['departure', 'destination'], ['price']); -> {note} All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. +> {note} All databases systems except SQL Server require the columns in the second argument provided to the `upsert` method to have a "primary" or "unique" index. ## Deleting Models -To delete a model, call the `delete` method on a model instance: +To delete a model, you may call the `delete` method on the model instance: - $flight = App\Models\Flight::find(1); + use App\Models\Flight; + + $flight = Flight::find(1); $flight->delete(); - -#### Deleting An Existing Model By Key + +#### Deleting An Existing Model By Its Primary Key -In the example above, we are retrieving the model from the database before calling the `delete` method. However, if you know the primary key of the model, you may delete the model without explicitly retrieving it by calling the `destroy` method. In addition to a single primary key as its argument, the `destroy` method will accept multiple primary keys, an array of primary keys, or a [collection](/docs/{{version}}/collections) of primary keys: +In the example above, we are retrieving the model from the database before calling the `delete` method. However, if you know the primary key of the model, you may delete the model without explicitly retrieving it by calling the `destroy` method. In addition to accepting the single primary key, the `destroy` method will accept multiple primary keys, an array of primary keys, or a [collection](/docs/{{version}}/collections) of primary keys: - App\Models\Flight::destroy(1); + Flight::destroy(1); - App\Models\Flight::destroy(1, 2, 3); + Flight::destroy(1, 2, 3); - App\Models\Flight::destroy([1, 2, 3]); + Flight::destroy([1, 2, 3]); - App\Models\Flight::destroy(collect([1, 2, 3])); + Flight::destroy(collect([1, 2, 3])); -> {note} The `destroy` method loads each model individually and calls the `delete` method on them so that the `deleting` and `deleted` events are fired. +> {note} The `destroy` method loads each model individually and calls the `delete` method so that the `deleting` and `deleted` events are properly dispatched for each model. - -#### Deleting Models By Query + +#### Deleting Models Using Queries -You can 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: +Of course, you may build an Eloquent query to delete all models matching your query's criteria. In this example, we will delete all flights that are marked as inactive. Like mass updates, mass deletes will not dispatch model events for the models that are deleted: - $deletedRows = App\Models\Flight::where('active', 0)->delete(); + $deletedRows = 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. +> {note} When executing a mass delete statement via Eloquent, the `deleting` and `deleted` model events will not be dispatched for the deleted models. This is because the models are never actually retrieved when executing the delete statement. ### Soft Deleting -In addition to actually removing records from your database, Eloquent can also "soft delete" models. When models are soft deleted, they are not actually removed from your database. Instead, a `deleted_at` attribute is set on the model and inserted into the database. If a model has a non-null `deleted_at` value, the model has been soft deleted. To enable soft deletes for a model, use the `Illuminate\Database\Eloquent\SoftDeletes` trait on the model: +In addition to actually removing records from your database, Eloquent can also "soft delete" models. When models are soft deleted, they are not actually removed from your database. Instead, a `deleted_at` attribute is set on the model indicating the date and time at which the model was "deleted". To enable soft deletes for a model, add the `Illuminate\Database\Eloquent\SoftDeletes` trait to the model: softDeletes(); - }); - } + use Illuminate\Database\Schema\Blueprint; + use Illuminate\Facades\Schema; - public function down() - { - Schema::table('flights', function (Blueprint $table) { - $table->dropSoftDeletes(); - }); - } + Schema::table('flights', function (Blueprint $table) { + $table->softDeletes(); + }); + + Schema::table('flights', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); -Now, when you call the `delete` method on the model, the `deleted_at` column will be set to the current date and time. And, when querying a model that uses soft deletes, the soft deleted models will automatically be excluded from all query results. +Now, when you call the `delete` method on the model, the `deleted_at` column will be set to the current date and time. However, the model's database record will be left in the table. When querying a model that uses soft deletes, the soft deleted models will automatically be excluded from all query results. -To determine if a given model instance has been soft deleted, use the `trashed` method: +To determine if a given model instance has been soft deleted, you may use the `trashed` method: if ($flight->trashed()) { // } - -### Querying Soft Deleted Models - - -#### Including Soft Deleted Models - -As noted above, soft deleted models will automatically be excluded from query results. However, you may force soft deleted models to appear in a result set using the `withTrashed` method on the query: - - $flights = App\Models\Flight::withTrashed() - ->where('account_id', 1) - ->get(); - -The `withTrashed` method may also be used on a [relationship](/docs/{{version}}/eloquent-relationships) query: - - $flight->history()->withTrashed()->get(); - - -#### Retrieving Only Soft Deleted Models - -The `onlyTrashed` method will retrieve **only** soft deleted models: - - $flights = App\Models\Flight::onlyTrashed() - ->where('airline_id', 1) - ->get(); - #### Restoring Soft Deleted Models -Sometimes you may wish to "un-delete" a soft deleted model. To restore a soft deleted model into an active state, use the `restore` method on a model instance: +Sometimes you may wish to "un-delete" a soft deleted model. To restore a soft deleted model, you may call the `restore` method on a model instance. The `restore` method will set the model's `deleted_at` column to `null`: $flight->restore(); -You may also use the `restore` method in a query to quickly restore multiple models. Again, like other "mass" operations, this will not fire any model events for the models that are restored: +You may also use the `restore` method in a query to restore multiple models. Again, like other "mass" operations, this will not dispatch any model events for the models that are restored: - App\Models\Flight::withTrashed() + Flight::withTrashed() ->where('airline_id', 1) ->restore(); -Like the `withTrashed` method, the `restore` method may also be used on [relationships](/docs/{{version}}/eloquent-relationships): +The `restore` method may also be used when building [relationship](/docs/{{version}}/eloquent-relationships) queries: $flight->history()->restore(); #### Permanently Deleting Models -Sometimes you may need to truly remove a model from your database. To permanently remove a soft deleted model from the database, use the `forceDelete` method: +Sometimes you may need to truly remove a model from your database. You may use the `forceDelete` method to permanently remove a soft deleted model from the database table: - // Force deleting a single model instance... $flight->forceDelete(); - // Force deleting all related models... +You may also use the `forceDelete` method when building Eloquent relationship queries: + $flight->history()->forceDelete(); + +### Querying Soft Deleted Models + + +#### Including Soft Deleted Models + +As noted above, soft deleted models will automatically be excluded from query results. However, you may force soft deleted models to be included in a query's results by calling the `withTrashed` method on the query: + + use App\Models\Flight; + + $flights = Flight::withTrashed() + ->where('account_id', 1) + ->get(); + +The `withTrashed` method may also be called when building a [relationship](/docs/{{version}}/eloquent-relationships) query: + + $flight->history()->withTrashed()->get(); + + +#### Retrieving Only Soft Deleted Models + +The `onlyTrashed` method will retrieve **only** soft deleted models: + + $flights = Flight::onlyTrashed() + ->where('airline_id', 1) + ->get(); + ## Replicating Models From 9c1b09ed3e27cb9d61d89588827bf7a89c04f037 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 15:12:50 -0600 Subject: [PATCH 179/274] wip --- eloquent.md | 122 ++++++++++++++++++++++++---------------------------- 1 file changed, 56 insertions(+), 66 deletions(-) diff --git a/eloquent.md b/eloquent.md index 3c8f59511f..cac8271fc1 100644 --- a/eloquent.md +++ b/eloquent.md @@ -820,9 +820,11 @@ The `onlyTrashed` method will retrieve **only** soft deleted models: ## Replicating Models -You may create an unsaved copy of a model instance using the `replicate` method. This is particularly useful when you have model instances that share many of the same attributes: +You may create an unsaved copy of an existing model instance using the `replicate` method. This method is particularly useful when you have model instances that share many of the same attributes: - $shipping = App\Models\Address::create([ + use App\Models\Address; + + $shipping = Address::create([ 'type' => 'shipping', 'line_1' => '123 Example Street', 'city' => 'Victorville', @@ -842,12 +844,14 @@ You may create an unsaved copy of a model instance using the `replicate` method. ### Global Scopes -Global scopes allow you to add constraints to all queries for a given model. Laravel's own [soft delete](#soft-deleting) functionality utilizes global scopes to only pull "non-deleted" models from the database. Writing your own global scopes can provide a convenient, easy way to make sure every query for a given model receives certain constraints. +Global scopes allow you to add constraints to all queries for a given model. Laravel's own [soft delete](#soft-deleting) functionality utilizes global scopes to only retrieve "non-deleted" models from the database. Writing your own global scopes can provide a convenient, easy way to make sure every query for a given model receives certain constraints. #### Writing Global Scopes -Writing a global scope is simple. Define a class that implements the `Illuminate\Database\Eloquent\Scope` interface. This interface requires you to implement one method: `apply`. The `apply` method may add `where` constraints to the query as needed: +Writing a global scope is simple. First, define a class that implements the `Illuminate\Database\Eloquent\Scope` interface. Laravel does not have a conventional location that you should place scope classes, so you are free to place this class in any directory that you wish. + +The `Scope` interface requires you to implement one method: `apply`. The `apply` method may add `where` constraints or other types of clauses to the query as needed: where('age', '>', 200); + $builder->where('created_at', '<', now()->subYears(2000)); } } @@ -877,13 +881,13 @@ Writing a global scope is simple. Define a class that implements the `Illuminate #### Applying Global Scopes -To assign a global scope to a model, you should override a given model's `booted` method and use the `addGlobalScope` method: +To assign a global scope to a model, you should override the model's `booted` method and invoke the model's `addGlobalScope` method. The `addGlobalScope` method accepts an instance of your scope as its only argument: 200 +```sql +select * from `users` where `age` > 200 +``` #### Anonymous Global Scopes -Eloquent also allows you to define global scopes using closures, which is particularly useful for simple scopes that do not warrant a separate class: +Eloquent also allows you to define global scopes using closures, which is particularly useful for simple scopes that do not warrant a separate class of their own. When defining a global scope using a closure, you should provide a scope name of your own choosing as the first argument to the `addGlobalScope` method: where('age', '>', 200); + static::addGlobalScope('ancient', function (Builder $builder) { + $builder->where('created_at', '<', now()->subYears(2000)); }); } } @@ -933,15 +939,15 @@ Eloquent also allows you to define global scopes using closures, which is partic #### 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: +If you would like to remove a global scope for a given query, you may use the `withoutGlobalScope` method. This method accepts the class name of the global scope as its only argument: - User::withoutGlobalScope(AgeScope::class)->get(); + User::withoutGlobalScope(AncientScope::class)->get(); -Or, if you defined the global scope using a closure: +Or, if you defined the global scope using a closure, you should pass the string name that you assigned to the global scope: - User::withoutGlobalScope('age')->get(); + User::withoutGlobalScope('ancient')->get(); -If you would like to remove several or even all of the global scopes, you may use the `withoutGlobalScopes` method: +If you would like to remove several or even all of the query's global scopes, you may use the `withoutGlobalScopes` method: // Remove all of the global scopes... User::withoutGlobalScopes()->get(); @@ -954,7 +960,7 @@ If you would like to remove several or even all of the global scopes, you may us ### Local Scopes -Local scopes allow you to define common sets of constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular". To define a scope, prefix an Eloquent model method with `scope`. +Local scopes allow you to define common sets of query constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular". To define a scope, prefix an Eloquent model method with `scope`. Scopes should always return a query builder instance: @@ -992,24 +998,26 @@ Scopes should always return a query builder instance: #### Utilizing A Local Scope -Once the scope has been defined, you may call the scope methods when querying the model. However, you should not include the `scope` prefix when calling the method. You can even chain calls to various scopes, for example: +Once the scope has been defined, you may call the scope methods when querying the model. However, you should not include the `scope` prefix when calling the method. You can even chain calls to various scopes: + + use App\Models\User; - $users = App\Models\User::popular()->active()->orderBy('created_at')->get(); + $users = User::popular()->active()->orderBy('created_at')->get(); -Combining multiple Eloquent model scopes via an `or` query operator may require the use of closure callbacks: +Combining multiple Eloquent model scopes via an `or` query operator may require the use of closures to achieve the correct [logical grouping](/docs/{{version}}/queries#logical-grouping): - $users = App\Models\User::popular()->orWhere(function (Builder $query) { + $users = User::popular()->orWhere(function (Builder $query) { $query->active(); })->get(); -However, since this can be cumbersome, Laravel provides a "higher order" `orWhere` method that allows you to fluently chain these scopes together without the use of closures: +However, since this can be cumbersome, Laravel provides a "higher order" `orWhere` method that allows you to fluently chain scopes together without the use of closures: $users = App\Models\User::popular()->orWhere->active()->get(); #### Dynamic Scopes -Sometimes you may wish to define a scope that accepts parameters. To get started, just add your additional parameters to your scope. Scope parameters should be defined after the `$query` parameter: +Sometimes you may wish to define a scope that accepts parameters. To get started, just add your additional parameters to your scope method's signature. Scope parameters should be defined after the `$query` parameter: get(); + $users = User::ofType('admin')->get(); ## Comparing Models @@ -1045,7 +1053,7 @@ Sometimes you may need to determine if two models are the "same". The `is` metho // } -The `is` method is also available when using the `belongsTo`, `hasOne`, `morphTo`, and `morphOne` relationships. This method is particularly helpful when you would like to compare a related model without issuing a query to retrieve that model: +The `is` method is also available when using the `belongsTo`, `hasOne`, `morphTo`, and `morphOne` [relationships](/docs/{{version}}/eloquent-relationships). This method is particularly helpful when you would like to compare a related model without issuing a query to retrieve that model: if ($post->author()->is($user)) { // @@ -1054,13 +1062,11 @@ The `is` method is also available when using the `belongsTo`, `hasOne`, `morphTo ## Events -Eloquent models fire several events, allowing you to hook into the following points in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `restoring`, `restored`, `replicating`. Events allow you to easily execute code each time a specific model class is saved or updated in the database. Each event receives the instance of the model through its constructor. - -The `retrieved` event will fire when an existing model is retrieved from the database. When a new model is saved for the first time, the `creating` and `created` events will fire. The `updating` / `updated` events will fire when an existing model is modified and the `save` method is called. The `saving` / `saved` events will fire when a model is created or updated. +Eloquent models dispatch several events, allowing you to hook into the following moments in a model's lifecycle: `retrieved`, `creating`, `created`, `updating`, `updated`, `saving`, `saved`, `deleting`, `deleted`, `restoring`, `restored`, and `replicating`. -> {note} When issuing a mass update or delete via Eloquent, the `saved`, `updated`, `deleting`, and `deleted` model events will not be fired for the affected models. This is because the models are never actually retrieved when issuing a mass update or delete. +The `retrieved` event will dispatch when an existing model is retrieved from the database. When a new model is saved for the first time, the `creating` and `created` events will dispatch. The `updating` / `updated` events will dispatch when an existing model is modified and the `save` method is called. The `saving` / `saved` events will dispatch when a model is created or updated. -To get started, define a `$dispatchesEvents` property on your Eloquent model that maps various points of the Eloquent model's lifecycle to your own [event classes](/docs/{{version}}/events): +To start listening to model events, define a `$dispatchesEvents` property on your Eloquent model. This property maps various points of the Eloquent model's lifecycle to your own [event classes](/docs/{{version}}/events). Each model event class should expect to receive an instance of the affected model via its constructor: {note} When issuing a mass update or delete query via Eloquent, the `saved`, `updated`, `deleting`, and `deleted` model events will not be dispatched for the affected models. This is because the models are never actually retrieved when performing a mass updates or deletes. + ### Using Closures -Instead of using custom event classes, you may register closures that execute when various model events are fired. Typically, you should register these closures in the `booted` method of your model: +Instead of using custom event classes, you may register closures that execute when various model events are dispatched. Typically, you should register these closures in the `booted` method of your model: #### Defining 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. Observer 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. The `make:observer` Artisan command is the easiest way to create a new observer class: +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. Observer classes have method names which reflect the Eloquent events you wish to listen for. Each of these methods receives the affected model as their only argument. The `make:observer` Artisan command is the easiest way to create a new observer class: php artisan make:observer UserObserver --model=User @@ -1186,43 +1194,25 @@ This command will place the new observer in your `App/Observers` directory. If t } } -To register an observer, use the `observe` method on the model you wish to observe. You may register observers in the `boot` method of one of your service providers. In this example, we'll register the observer in the `AppServiceProvider`: +To register an observer, you need to call the `observe` method on the model you wish to observe. You may register observers in the `boot` method of your application's `App\Providers\EventServiceProvider` service provider: - ### Muting Events -You may occasionally wish to temporarily "mute" all events fired by a model. You may achieve this using the `withoutEvents` method. The `withoutEvents` method accepts a closure as its only argument. Any code executed within this closure will not fire model events. For example, the following will fetch and delete an `App\Models\User` instance without firing any model events. Any value returned by the given closure will be returned by the `withoutEvents` method: +You may occasionally need to temporarily "mute" all events fired by a model. You may achieve this using the `withoutEvents` method. The `withoutEvents` method accepts a closure as its only argument. Any code executed within this closure will not dispatch model events. For example, the following example will fetch and delete an `App\Models\User` instance without dispatching any model events. Any value returned by the closure will be returned by the `withoutEvents` method: use App\Models\User; @@ -1235,7 +1225,7 @@ You may occasionally wish to temporarily "mute" all events fired by a model. You #### Saving A Single Model Without Events -Sometimes you may wish to "save" a given model without raising any events. You may accomplish this using the `saveQuietly` method: +Sometimes you may wish to "save" a given model without dispatching any events. You may accomplish this using the `saveQuietly` method: $user = User::findOrFail(1); From 44a8ffb731d58e09b27a41bc6861cca934cd6c5b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 15:17:26 -0600 Subject: [PATCH 180/274] wip --- eloquent.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/eloquent.md b/eloquent.md index cac8271fc1..73b0fbd221 100644 --- a/eloquent.md +++ b/eloquent.md @@ -514,6 +514,16 @@ Of course, when using Eloquent, we don't only need to retrieve models from the d In this example, we assign the `name` field from the incoming HTTP request to the `name` attribute of the `App\Models\Flight` model instance. When we call the `save` method, a record will be inserted into the database. The model's `created_at` and `updated_at` timestamps will automatically be set when the `save` method is called, so there is no need to set them manually. +Alternatively, you may use the `create` method to "save" a new model using a single PHP statement. The inserted model instance will be returned to you by the `create` method: + + use App\Models\Flight; + + $flight = Flight::create([ + 'name' => 'London to Paris', + ]); + +However, before using the `create` method, you will need to specify either a `fillable` or `guarded` property on your model class. These properties are required because all Eloquent models are protected against mass assignment vulnerabilities by default. To learn more about mass assignment, please consult the [mass assignment documentation](#mass-assignment). + ### Updates @@ -602,7 +612,7 @@ The `getOriginal` method returns an array containing the original attributes of ### Mass Assignment -You may use the `create` method to save a new model using a single PHP statement. The inserted model instance will be returned to you from the method: +You may use the `create` method to "save" a new model using a single PHP statement. The inserted model instance will be returned to you by the method: use App\Models\Flight; From d72a829753283181d1f6708b669774750ac2bf79 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 15:44:26 -0600 Subject: [PATCH 181/274] wip --- eloquent-relationships.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 4429dc8460..f3d1f4c123 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -35,7 +35,7 @@ ## Introduction -Database tables are often related to one another. For example, a blog post may have many comments, or an order could be related to the user who placed it. Eloquent makes managing and working with these relationships easy, and supports several different types of relationships: +Database tables are often related to one another. For example, a blog post may have many comments or an order could be related to the user who placed it. Eloquent makes managing and working with these relationships easy, and supports a variety of common relationships:
- [One To One](#one-to-one) @@ -51,18 +51,16 @@ Database tables are often related to one another. For example, a blog post may h ## Defining Relationships -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: +Eloquent relationships are defined as methods on your Eloquent model classes. Since 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 query constraints on this `posts` relationship: $user->posts()->where('active', 1)->get(); -But, before diving too deep into using relationships, let's learn how to define each type. - -> {note} Relationship names cannot collide with attribute names as that could lead to your model not being able to know which one to resolve. +But, before diving too deep into using relationships, let's learn how to define each type of relationship supported by Eloquent. ### One To One -A one-to-one relationship is a very basic relation. For example, a `User` model might be associated with one `Phone`. To define this relationship, we place a `phone` method on the `User` model. The `phone` method should call the `hasOne` method and return its result: +A one-to-one relationship is a very basic type of database relationship. For example, a `User` model might be associated with one `Phone` model. To define this relationship, we will place a `phone` method on the `User` model. The `phone` method should call the `hasOne` method and return its result. The `hasOne` method is available to your model via the model's `Illuminate\Database\Eloquent\Model` base class: hasOne('App\Models\Phone'); + return $this->hasOne(Phone::class); } } -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: +The first argument passed to the `hasOne` method is the name of the related model class. 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; -Eloquent determines the foreign key of the relationship based on the model name. In this case, the `Phone` model is automatically assumed to have a `user_id` foreign key. If you wish to override this convention, you may pass a second argument to the `hasOne` method: +Eloquent determines the foreign key of the relationship based on the parent model name. In this case, the `Phone` model is automatically assumed to have a `user_id` foreign key. If you wish to override this convention, you may pass a second argument to the `hasOne` method: - return $this->hasOne('App\Models\Phone', 'foreign_key'); + return $this->hasOne(Phone::class, 'foreign_key'); -Additionally, Eloquent assumes that the foreign key should have a value matching the `id` (or the custom `$primaryKey`) column of the parent. In other words, Eloquent will look for the value of the user's `id` column in the `user_id` column of the `Phone` record. If you would like the relationship to use a value other than `id`, you may pass a third argument to the `hasOne` method specifying your custom key: +Additionally, Eloquent assumes that the foreign key should have a value matching the primary key column of the parent. In other words, Eloquent will look for the value of the user's `id` column in the `user_id` column of the `Phone` record. If you would like the relationship to use a value other than `id` or your model's `$primaryKey` property, you may pass a third argument to the `hasOne` method: - return $this->hasOne('App\Models\Phone', 'foreign_key', 'local_key'); + return $this->hasOne(Phone::class, 'foreign_key', 'local_key'); #### Defining The Inverse Of The Relationship -So, we can access the `Phone` model from our `User`. Now, let's define a relationship on the `Phone` model that will let us access the `User` that owns the phone. We can define the inverse of a `hasOne` relationship using the `belongsTo` method: +So, we can access the `Phone` model from our `User` model. Next, let's define a relationship on the `Phone` model that will let us access the `User` that owns the phone. We can define the inverse of a `hasOne` relationship using the `belongsTo` method: belongsTo('App\Models\User'); + return $this->belongsTo(User::class); } } -In the example above, Eloquent will try to match the `user_id` from the `Phone` model to an `id` on the `User` model. Eloquent determines the default foreign key name by examining the name of the relationship method and suffixing the method name with `_id`. However, if the foreign key on the `Phone` model is not `user_id`, you may pass a custom key name as the second argument to the `belongsTo` method: +When invoking the `user` method, Eloquent will attempt to find a `User` model that has an `id` which matches the `user_id` column on the `Phone` model. + +Eloquent determines the foreign key name by examining the name of the relationship method and suffixing the method name with `_id`. So, in this case, Eloquent assumes that the `Phone` model has a `user_id` column. However, if the foreign key on the `Phone` model is not `user_id`, you may pass a custom key name as the second argument to the `belongsTo` method: /** * Get the user that owns the phone. */ public function user() { - return $this->belongsTo('App\Models\User', 'foreign_key'); + return $this->belongsTo(User::class, 'foreign_key'); } -If your parent model does not use `id` as its primary key, or you wish to join the child model to a different column, you may pass a third argument to the `belongsTo` method specifying your parent table's custom key: +If the parent model does not use `id` as its primary key, or you wish to find the associated model using a different column, you may pass a third argument to the `belongsTo` method specifying the parent table's custom key: /** * Get the user that owns the phone. */ public function user() { - return $this->belongsTo('App\Models\User', 'foreign_key', 'owner_key'); + return $this->belongsTo(User::class, 'foreign_key', 'owner_key'); } From f38448a5a589a6cdb1495630c799e08b8cb31dd0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 16:03:01 -0600 Subject: [PATCH 182/274] wip --- eloquent-relationships.md | 54 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index f3d1f4c123..fd98177aa2 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -4,7 +4,7 @@ - [Defining Relationships](#defining-relationships) - [One To One](#one-to-one) - [One To Many](#one-to-many) - - [One To Many (Inverse)](#one-to-many-inverse) + - [One To Many (Inverse) / Belongs To](#one-to-many-inverse) - [Many To Many](#many-to-many) - [Defining Custom Intermediate Table Models](#defining-custom-intermediate-table-models) - [Has One Through](#has-one-through) @@ -87,14 +87,14 @@ Eloquent determines the foreign key of the relationship based on the parent mode return $this->hasOne(Phone::class, 'foreign_key'); -Additionally, Eloquent assumes that the foreign key should have a value matching the primary key column of the parent. In other words, Eloquent will look for the value of the user's `id` column in the `user_id` column of the `Phone` record. If you would like the relationship to use a value other than `id` or your model's `$primaryKey` property, you may pass a third argument to the `hasOne` method: +Additionally, Eloquent assumes that the foreign key should have a value matching the primary key column of the parent. In other words, Eloquent will look for the value of the user's `id` column in the `user_id` column of the `Phone` record. If you would like the relationship to use a primary key value other than `id` or your model's `$primaryKey` property, you may pass a third argument to the `hasOne` method: return $this->hasOne(Phone::class, 'foreign_key', 'local_key'); #### Defining The Inverse Of The Relationship -So, we can access the `Phone` model from our `User` model. Next, let's define a relationship on the `Phone` model that will let us access the `User` that owns the phone. We can define the inverse of a `hasOne` relationship using the `belongsTo` method: +So, we can access the `Phone` model from our `User` model. Next, let's define a relationship on the `Phone` model that will let us access the user that owns the phone. We can define the inverse of a `hasOne` relationship using the `belongsTo` method: ### One To Many -A one-to-many relationship is used to define relationships where a single model owns any amount of other models. For example, a blog post may have an infinite number of comments. Like all other Eloquent relationships, one-to-many relationships are defined by placing a function on your Eloquent model: +A one-to-many relationship is used to define relationships where a single model is the parent to one or more child models. For example, a blog post may have an infinite number of comments. Like all other Eloquent relationships, one-to-many relationships are defined by defining a method on your Eloquent model: hasMany('App\Models\Comment'); + return $this->hasMany(Comment::class); } } -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`. +Remember, Eloquent will automatically determine the proper foreign key column for the `Comment` model. By convention, Eloquent will take the "snake case" name of the parent model and suffix it with `_id`. So, in this example, Eloquent will assume the foreign key column 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 methods as if they were defined as properties on the model: +Once the relationship method has been defined, we can access the [collection](/docs/{{version}}/eloquent-collections) of related comments by accessing the `comments` property. Remember, since Eloquent provides "dynamic relationship properties", we can access relationship methods as if they were defined as properties on the model: - $comments = App\Models\Post::find(1)->comments; + use App\Models\Post; + + $comments = Post::find(1)->comments; foreach ($comments as $comment) { // } -Since all relationships also serve as query builders, you can add further constraints to which comments are retrieved by calling the `comments` method and continuing to chain conditions onto the query: +Since all relationships also serve as query builders, you may add further constraints to the relationship query by calling the `comments` method and continuing to chain conditions onto the query: - $comment = App\Models\Post::find(1)->comments()->where('title', 'foo')->first(); + $comment = Post::find(1)->comments() + ->where('title', 'foo') + ->first(); Like the `hasOne` method, you may also override the foreign and local keys by passing additional arguments to the `hasMany` method: - return $this->hasMany('App\Models\Comment', 'foreign_key'); + return $this->hasMany(Comment::class, 'foreign_key'); - return $this->hasMany('App\Models\Comment', 'foreign_key', 'local_key'); + return $this->hasMany(Comment::class, 'foreign_key', 'local_key'); -### One To Many (Inverse) +### One To Many (Inverse) / Belongs To -Now that we can access all of a post's comments, let's define a relationship to allow a comment to access its parent post. To define the inverse of a `hasMany` relationship, define a relationship function on the child model which calls the `belongsTo` method: +Now that we can access all of a post's comments, let's define a relationship to allow a comment to access its parent post. To define the inverse of a `hasMany` relationship, define a relationship method on the child model which calls the `belongsTo` method: belongsTo('App\Models\Post'); + return $this->belongsTo(Post::class); } } -Once the relationship has been defined, we can retrieve the `Post` model for a `Comment` by accessing the `post` "dynamic property": +Once the relationship has been defined, we can retrieve a comment's parent post by accessing the `post` "dynamic relationship property": - $comment = App\Models\Comment::find(1); + use App\Models\Comment; + + $comment = Comment::find(1); + + return $comment->post->title; + +In the example above, Eloquent will attempt to find a `Post` model that has an `id` which matches the `post_id` column on the `Comment` model. - echo $comment->post->title; +Eloquent determines the default foreign key name by examining the name of the relationship method and suffixing the method name with a `_` followed by the name of the parent model's primary key column. So, in this example, Eloquent will assume the `Post` model's foreign key on the `comments` table is `post_id`. -In the example above, Eloquent will try to match the `post_id` from the `Comment` model to an `id` on the `Post` model. Eloquent determines the default foreign key name by examining the name of the relationship method and suffixing the method name with a `_` followed by the name of the primary key column. However, if the foreign key on the `Comment` model is not `post_id`, you may pass a custom key name as the second argument to the `belongsTo` method: +However, if the foreign key for your relationship does not follow these conventions, you may pass a custom foreign key name as the second argument to the `belongsTo` method: /** * Get the post that owns the comment. */ public function post() { - return $this->belongsTo('App\Models\Post', 'foreign_key'); + return $this->belongsTo(Post::class, 'foreign_key'); } -If your parent model does not use `id` as its primary key, or you wish to join the child model to a different column, you may pass a third argument to the `belongsTo` method specifying your parent table's custom key: +If your parent model does not use `id` as its primary key, or you wish to find the associated model using a different column, you may pass a third argument to the `belongsTo` method specifying your parent table's custom key: /** * Get the post that owns the comment. */ public function post() { - return $this->belongsTo('App\Models\Post', 'foreign_key', 'owner_key'); + return $this->belongsTo(Post::class, 'foreign_key', 'owner_key'); } From 6b002eb0835fa7fd02422ae323b6ddace96893dc Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 16:46:06 -0600 Subject: [PATCH 183/274] wip --- eloquent-relationships.md | 361 ++++++++++++++++++-------------------- 1 file changed, 175 insertions(+), 186 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index fd98177aa2..fa6b58799d 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -5,10 +5,12 @@ - [One To One](#one-to-one) - [One To Many](#one-to-many) - [One To Many (Inverse) / Belongs To](#one-to-many-inverse) - - [Many To Many](#many-to-many) - - [Defining Custom Intermediate Table Models](#defining-custom-intermediate-table-models) - [Has One Through](#has-one-through) - [Has Many Through](#has-many-through) +- [Many To Many Relationships](#many-to-many) + - [Retrieving Intermediate Table Columns](#retrieving-intermediate-table-columns) + - [Filtering Queries Via Intermediate Table Columns](#filtering-queries-via-intermediate-table-columns) + - [Defining Custom Intermediate Table Models](#defining-custom-intermediate-table-models) - [Polymorphic Relationships](#polymorphic-relationships) - [One To One](#one-to-one-polymorphic-relations) - [One To Many](#one-to-many-polymorphic-relations) @@ -235,15 +237,138 @@ If your parent model does not use `id` as its primary key, or you wish to find t return $this->belongsTo(Post::class, 'foreign_key', 'owner_key'); } + +### Has One Through + +The "has-one-through" relationship links models through a single intermediate relation. + +For example, in a vehicle repair shop application, each `Mechanic` may have one `Car`, and each `Car` may have one `Owner`. While the `Mechanic` and the `Owner` have no direct connection, the `Mechanic` can access the `Owner` _through_ the `Car` itself. Let's look at the tables necessary to define this relationship: + + mechanics + id - integer + name - string + + cars + id - integer + model - string + mechanic_id - integer + + owners + id - integer + name - string + car_id - integer + +Now that we have examined the table structure for the relationship, let's define the relationship on the `Mechanic` model: + + hasOneThrough('App\Models\Owner', 'App\Models\Car'); + } + } + +The first argument passed to the `hasOneThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. + +Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasOneThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: + + class Mechanic extends Model + { + /** + * Get the car's owner. + */ + public function carOwner() + { + return $this->hasOneThrough( + 'App\Models\Owner', + 'App\Models\Car', + 'mechanic_id', // Foreign key on cars table... + 'car_id', // Foreign key on owners table... + 'id', // Local key on mechanics table... + 'id' // Local key on cars table... + ); + } + } + + +### Has Many Through + +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 + name - string + + users + id - integer + country_id - integer + name - string + + posts + id - integer + user_id - integer + title - string + +Though `posts` does not contain a `country_id` column, the `hasManyThrough` relation provides access to a country's posts via `$country->posts`. To perform this query, Eloquent inspects the `country_id` on the intermediate `users` table. After finding the matching user IDs, they are used to query the `posts` table. + +Now that we have examined the table structure for the relationship, let's define it on the `Country` model: + + hasManyThrough('App\Models\Post', 'App\Models\User'); + } + } + +The first argument passed to the `hasManyThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. + +Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasManyThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: + + class Country extends Model + { + public function posts() + { + return $this->hasManyThrough( + 'App\Models\Post', + 'App\Models\User', + 'country_id', // Foreign key on users table... + 'user_id', // Foreign key on posts table... + 'id', // Local key on countries table... + 'id' // Local key on users table... + ); + } + } + -### Many To Many +## Many To Many Relationships -Many-to-many relations are slightly more complicated than `hasOne` and `hasMany` relationships. An example of such a relationship is a user with many roles, where the roles are also shared by other users. For example, many users may have the role of "Admin". +Many-to-many relations are slightly more complicated than `hasOne` and `hasMany` relationships. An example of a many-to-many relationship is a user that has many roles and those roles are also shared by other users in the application. For example, a user may be assigned the role of "Author" and "Editor"; however, those roles may also be assigned to other users as well. So, a user has many roles and a role has many users. #### Table Structure -To define this relationship, three database tables are needed: `users`, `roles`, and `role_user`. The `role_user` table is derived from the alphabetical order of the related model names, and contains the `user_id` and `role_id` columns: +To define this relationship, three database tables are needed: `users`, `roles`, and `role_user`. The `role_user` table is derived from the alphabetical order of the related model names and contains `user_id` and `role_id` columns. This table is used as an intermediate table linking the users and roles. + +Remember, since a role can belong to many users, we cannot simply place a `user_id` column on the `roles` table. This would mean that a role could only belong to a single user. In order to provide support for roles being assigned to multiple users, the `role_user` table is needed. We can summarize the relationship's table structure like so: users id - integer @@ -260,7 +385,7 @@ To define this relationship, three database tables are needed: `users`, `roles`, #### Model Structure -Many-to-many relationships are defined by writing a method that returns the result of the `belongsToMany` method. For example, let's define the `roles` method on our `User` model: +Many-to-many relationships are defined by writing a method that returns the result of the `belongsToMany` method. The `belongsToMany` method is provided by the `Illuminate\Database\Eloquent\Model` base class that is used by all of your application's Eloquent models. For example, let's define a `roles` method on our `User` model. The first argument passed to this method is the name of the related model class: belongsToMany('App\Models\Role'); + return $this->belongsToMany(Role::class); } } -Once the relationship is defined, you may access the user's roles using the `roles` dynamic property: +Once the relationship is defined, you may access the user's roles using the `roles` dynamic relationship property: - $user = App\Models\User::find(1); + use App\Models\User; + + $user = User::find(1); foreach ($user->roles as $role) { // } -Like all other relationship types, you may call the `roles` method to continue chaining query constraints onto the relationship: +Since all relationships also serve as query builders, you may add further constraints to the relationship query by calling the `roles` method and continuing to chain conditions onto the query: - $roles = App\Models\User::find(1)->roles()->orderBy('name')->get(); + $roles = User::find(1)->roles()->orderBy('name')->get(); -As mentioned previously, to determine the table name of the relationship's joining table, Eloquent will join the two related model names in alphabetical order. However, you are free to override this convention. You may do so by passing a second argument to the `belongsToMany` method: +To determine the table name of the relationship's intermediate table, Eloquent will join the two related model names in alphabetical order. However, you are free to override this convention. You may do so by passing a second argument to the `belongsToMany` method: - return $this->belongsToMany('App\Models\Role', 'role_user'); + return $this->belongsToMany(Role::class, 'role_user'); -In addition to customizing the name of the joining table, you may also customize the column names of the keys on the table by passing additional arguments to the `belongsToMany` method. The third argument is the foreign key name of the model on which you are defining the relationship, while the fourth argument is the foreign key name of the model that you are joining to: +In addition to customizing the name of the intermediate table, you may also customize the column names of the keys on the table by passing additional arguments to the `belongsToMany` method. The third argument is the foreign key name of the model on which you are defining the relationship, while the fourth argument is the foreign key name of the model that you are joining to: - return $this->belongsToMany('App\Models\Role', 'role_user', 'user_id', 'role_id'); + return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id'); #### Defining The Inverse Of The Relationship -To define the inverse of a many-to-many relationship, you place another call to `belongsToMany` on your related model. To continue our user roles example, let's define the `users` method on the `Role` model: +To define the "inverse" of a many-to-many relationship, you should define a method on the related model which also returns the result of the `belongsToMany` method. To complete our user / role example, let's define the `users` method on the `Role` model: belongsToMany('App\Models\User'); + return $this->belongsToMany(User::class); } } -As you can see, the relationship is defined exactly the same as its `User` counterpart, with the exception of referencing the `App\Models\User` model. Since we're reusing the `belongsToMany` method, all of the usual table and key customization options are available when defining the inverse of many-to-many relationships. +As you can see, the relationship is defined exactly the same as its `User` model counterpart with the exception of referencing the `App\Models\User` model. Since we're reusing the `belongsToMany` method, all of the usual table and key customization options are available when defining the "inverse" of many-to-many relationships. -#### Retrieving Intermediate Table Columns +### Retrieving Intermediate Table Columns -As you have already learned, working with many-to-many relations requires the presence of an intermediate table. Eloquent provides some very helpful ways of interacting with this table. For example, let's assume our `User` object has many `Role` objects that it is related to. After accessing this relationship, we may access the intermediate table using the `pivot` attribute on the models: +As you have already learned, working with many-to-many relations requires the presence of an intermediate table. Eloquent provides some very helpful ways of interacting with this table. For example, let's assume our `User` model has many `Role` models that it is related to. After accessing this relationship, we may access the intermediate table using the `pivot` attribute on the models: - $user = App\Models\User::find(1); + use App\Models\User; + + $user = User::find(1); foreach ($user->roles as $role) { echo $role->pivot->created_at; } -Notice that each `Role` model we retrieve is automatically assigned a `pivot` attribute. This attribute contains a model representing the intermediate table, and may be used like any other Eloquent model. +Notice that each `Role` model we retrieve is automatically assigned a `pivot` attribute. This attribute contains a model representing the intermediate table. -By default, only the model keys will be present on the `pivot` object. If your pivot table contains extra attributes, you must specify them when defining the relationship: +By default, only the model keys will be present on the `pivot` model. If your intermediate table contains extra attributes, you must specify them when defining the relationship: - return $this->belongsToMany('App\Models\Role')->withPivot('column1', 'column2'); + return $this->belongsToMany(Role::class)->withPivot('active', 'created_by'); -If you want your pivot table to have automatically maintained `created_at` and `updated_at` timestamps, use the `withTimestamps` method on the relationship definition: +If you would like your intermediate table to have `created_at` and `updated_at` timestamps that are automatically maintained by Eloquent, call the `withTimestamps` method when defining the relationship: - return $this->belongsToMany('App\Models\Role')->withTimestamps(); + return $this->belongsToMany(Role::class)->withTimestamps(); -> {note} When using timestamps on pivot tables, the table is required to have both `created_at` and `updated_at` timestamp columns. +> {note} Intermediate tables that utilize Eloquent's automatically maintained timestamps are required to have both `created_at` and `updated_at` timestamp columns. #### Customizing The `pivot` Attribute Name -As noted earlier, attributes from the intermediate table may be accessed on models using the `pivot` attribute. However, you are free to customize the name of this attribute to better reflect its purpose within your application. +As noted previously, attributes from the intermediate table may be accessed on models via the `pivot` attribute. However, you are free to customize the name of this attribute to better reflect its purpose within your application. -For example, if your application contains users that may subscribe to podcasts, you probably have a many-to-many relationship between users and podcasts. If this is the case, you may wish to rename your intermediate table accessor to `subscription` instead of `pivot`. This can be done using the `as` method when defining the relationship: +For example, if your application contains users that may subscribe to podcasts, you likely have a many-to-many relationship between users and podcasts. If this is the case, you may wish to rename your intermediate table attribute to `subscription` instead of `pivot`. This can be done using the `as` method when defining the relationship: - return $this->belongsToMany('App\Models\Podcast') + return $this->belongsToMany(Podcast::class) ->as('subscription') ->withTimestamps(); -Once this is done, you may access the intermediate table data using the customized name: +Once the custom intermediate table attribute has been specified, you may access the intermediate table data using the customized name: $users = User::with('podcasts')->get(); @@ -365,21 +494,26 @@ Once this is done, you may access the intermediate table data using the customiz echo $podcast->subscription->created_at; } - -#### Filtering Relationships Via Intermediate Table Columns + +### Filtering Queries Via Intermediate Table Columns -You can also filter the results returned by `belongsToMany` using the `wherePivot`, `wherePivotIn`, and `wherePivotNotIn` methods when defining the relationship: +You can also filter the results returned by `belongsToMany` relationship queries using the `wherePivot`, `wherePivotIn`, and `wherePivotNotIn` methods when defining the relationship: - return $this->belongsToMany('App\Models\Role')->wherePivot('approved', 1); + return $this->belongsToMany(Role::class) + ->wherePivot('approved', 1); - return $this->belongsToMany('App\Models\Role')->wherePivotIn('priority', [1, 2]); + return $this->belongsToMany(Role::class) + ->wherePivotIn('priority', [1, 2]); - return $this->belongsToMany('App\Models\Role')->wherePivotNotIn('priority', [1, 2]); + return $this->belongsToMany(Role::class) + ->wherePivotNotIn('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. Custom many-to-many pivot models should extend the `Illuminate\Database\Eloquent\Relations\Pivot` class while custom polymorphic many-to-many pivot models should extend the `Illuminate\Database\Eloquent\Relations\MorphPivot` class. For example, we may define a `Role` which uses a custom `RoleUser` pivot model: +If you would like to define a custom model to represent the intermediate table of your many-to-many relationship, you may call the `using` method when defining the relationship. Custom pivot models give you the opportunity to define additional methods on the pivot model. + +Custom many-to-many pivot models should extend the `Illuminate\Database\Eloquent\Relations\Pivot` class while custom polymorphic many-to-many pivot models should extend the `Illuminate\Database\Eloquent\Relations\MorphPivot` class. For example, we may define a `Role` model which uses a custom `RoleUser` pivot model: belongsToMany('App\Models\User')->using('App\Models\RoleUser'); + return $this->belongsToMany(User::class)->using(RoleUser::class); } } -When defining the `RoleUser` model, we will extend the `Pivot` class: +When defining the `RoleUser` model, you should extend the `Illuminate\Database\Eloquent\Relations\Pivot` class: belongsToMany('App\Models\User') - ->using('App\Models\RoleUser') - ->withPivot([ - 'created_by', - 'updated_by', - ]); - } - } - -> **Note:** Pivot models may not use the `SoftDeletes` trait. If you need to soft delete pivot records consider converting your pivot model to an actual Eloquent model. +> {note} Pivot models may not use the `SoftDeletes` trait. If you need to soft delete pivot records consider converting your pivot model to an actual Eloquent model. #### Custom Pivot Models And Incrementing IDs @@ -449,127 +559,6 @@ If you have defined a many-to-many relationship that uses a custom pivot model, */ public $incrementing = true; - -### Has One Through - -The "has-one-through" relationship links models through a single intermediate relation. - -For example, in a vehicle repair shop application, each `Mechanic` may have one `Car`, and each `Car` may have one `Owner`. While the `Mechanic` and the `Owner` have no direct connection, the `Mechanic` can access the `Owner` _through_ the `Car` itself. Let's look at the tables necessary to define this relationship: - - mechanics - id - integer - name - string - - cars - id - integer - model - string - mechanic_id - integer - - owners - id - integer - name - string - car_id - integer - -Now that we have examined the table structure for the relationship, let's define the relationship on the `Mechanic` model: - - hasOneThrough('App\Models\Owner', 'App\Models\Car'); - } - } - -The first argument passed to the `hasOneThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. - -Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasOneThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: - - class Mechanic extends Model - { - /** - * Get the car's owner. - */ - public function carOwner() - { - return $this->hasOneThrough( - 'App\Models\Owner', - 'App\Models\Car', - 'mechanic_id', // Foreign key on cars table... - 'car_id', // Foreign key on owners table... - 'id', // Local key on mechanics table... - 'id' // Local key on cars table... - ); - } - } - - -### Has Many Through - -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 - name - string - - users - id - integer - country_id - integer - name - string - - posts - id - integer - user_id - integer - title - string - -Though `posts` does not contain a `country_id` column, the `hasManyThrough` relation provides access to a country's posts via `$country->posts`. To perform this query, Eloquent inspects the `country_id` on the intermediate `users` table. After finding the matching user IDs, they are used to query the `posts` table. - -Now that we have examined the table structure for the relationship, let's define it on the `Country` model: - - hasManyThrough('App\Models\Post', 'App\Models\User'); - } - } - -The first argument passed to the `hasManyThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. - -Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasManyThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: - - class Country extends Model - { - public function posts() - { - return $this->hasManyThrough( - 'App\Models\Post', - 'App\Models\User', - 'country_id', // Foreign key on users table... - 'user_id', // Foreign key on posts table... - 'id', // Local key on countries table... - 'id' // Local key on users table... - ); - } - } - ## Polymorphic Relationships From 83f622acbe5f71c2515c0cb50946c01e7c6328f4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 16:58:29 -0600 Subject: [PATCH 184/274] wip --- eloquent-relationships.md | 46 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index fa6b58799d..57c77bc2af 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -240,9 +240,9 @@ If your parent model does not use `id` as its primary key, or you wish to find t ### Has One Through -The "has-one-through" relationship links models through a single intermediate relation. +The "has-one-through" relationship defines a one-to-one relationship with another model. However, this relationship indicates that the declaring model can be matched with one instance of another model by proceeding _through_ a third model. -For example, in a vehicle repair shop application, each `Mechanic` may have one `Car`, and each `Car` may have one `Owner`. While the `Mechanic` and the `Owner` have no direct connection, the `Mechanic` can access the `Owner` _through_ the `Car` itself. Let's look at the tables necessary to define this relationship: +For example, in a vehicle repair shop application, each `Mechanic` model may be associated with one `Car` model, and each `Car` model may be associated with one `Owner` model. While the `Mechanic` and the `Owner` have no direct connection, the `Mechanic` can access the `Owner` _through_ the `Car` model. Let's look at the tables necessary to define this relationship: mechanics id - integer @@ -273,12 +273,15 @@ Now that we have examined the table structure for the relationship, let's define */ public function carOwner() { - return $this->hasOneThrough('App\Models\Owner', 'App\Models\Car'); + return $this->hasOneThrough(Owner::class, Car::class); } } The first argument passed to the `hasOneThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. + +#### Key Conventions + Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasOneThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: class Mechanic extends Model @@ -289,12 +292,12 @@ Typical Eloquent foreign key conventions will be used when performing the relati public function carOwner() { return $this->hasOneThrough( - 'App\Models\Owner', - 'App\Models\Car', - 'mechanic_id', // Foreign key on cars table... - 'car_id', // Foreign key on owners table... - 'id', // Local key on mechanics table... - 'id' // Local key on cars table... + Owner::class, + Car::class, + 'mechanic_id', // Foreign key on the cars table... + 'car_id', // Foreign key on the owners table... + 'id', // Local key on the mechanics table... + 'id' // Local key on the cars table... ); } } @@ -302,7 +305,7 @@ Typical Eloquent foreign key conventions will be used when performing the relati ### Has Many Through -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: +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. Using 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 @@ -318,9 +321,7 @@ The "has-many-through" relationship provides a convenient shortcut for accessing user_id - integer title - string -Though `posts` does not contain a `country_id` column, the `hasManyThrough` relation provides access to a country's posts via `$country->posts`. To perform this query, Eloquent inspects the `country_id` on the intermediate `users` table. After finding the matching user IDs, they are used to query the `posts` table. - -Now that we have examined the table structure for the relationship, let's define it on the `Country` model: +Now that we have examined the table structure for the relationship, let's define the relationship on the `Country` model: hasManyThrough('App\Models\Post', 'App\Models\User'); + return $this->hasManyThrough(Post::class, User::class); } } The first argument passed to the `hasManyThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. +Though the `Post` model's table does not contain a `country_id` column, the `hasManyThrough` relation provides access to a country's posts via `$country->posts`. To retrieve these models, Eloquent inspects the `country_id` column on the intermediate `User` model's table. After finding the relevant user IDs, they are used to query the `Post` model's table. + + +#### Key Conventions + Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasManyThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: class Country extends Model @@ -348,12 +354,12 @@ Typical Eloquent foreign key conventions will be used when performing the relati public function posts() { return $this->hasManyThrough( - 'App\Models\Post', - 'App\Models\User', - 'country_id', // Foreign key on users table... - 'user_id', // Foreign key on posts table... - 'id', // Local key on countries table... - 'id' // Local key on users table... + Post::class, + User::class, + 'country_id', // Foreign key on the users table... + 'user_id', // Foreign key on the posts table... + 'id', // Local key on the countries table... + 'id' // Local key on the users table... ); } } From 760bf5bbc7790d563c8f3f4fe87903a28c0e7981 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 25 Nov 2020 17:05:13 -0600 Subject: [PATCH 185/274] use better example --- eloquent-relationships.md | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 57c77bc2af..06ad847380 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -305,23 +305,23 @@ Typical Eloquent foreign key conventions will be used when performing the relati ### Has Many Through -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. Using 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 way to access distant relations via an intermediate relation. For example, let's assume we are building a deployment platform like [Laravel Vapor](https://vapor.laravel.com). A `Project` model might access many `Deployment` models through an intermediate `Environment` model. Using this example, you could easily gather all deployments for a given environment. Let's look at the tables required to define this relationship: - countries + projects id - integer name - string - users + environments id - integer - country_id - integer + project_id - integer name - string - posts + deployments id - integer - user_id - integer - title - string + environment_id - integer + commit_hash - string -Now that we have examined the table structure for the relationship, let's define the relationship on the `Country` model: +Now that we have examined the table structure for the relationship, let's define the relationship on the `Project` model: hasManyThrough(Post::class, User::class); + return $this->hasManyThrough(Deployment::class, Environment::class); } } The first argument passed to the `hasManyThrough` method is the name of the final model we wish to access, while the second argument is the name of the intermediate model. -Though the `Post` model's table does not contain a `country_id` column, the `hasManyThrough` relation provides access to a country's posts via `$country->posts`. To retrieve these models, Eloquent inspects the `country_id` column on the intermediate `User` model's table. After finding the relevant user IDs, they are used to query the `Post` model's table. +Though the `Deployment` model's table does not contain a `project_id` column, the `hasManyThrough` relation provides access to a project's deployments via `$project->deployments`. To retrieve these models, Eloquent inspects the `project_id` column on the intermediate `Environment` model's table. After finding the relevant environment IDs, they are used to query the `Deployment` model's table. #### Key Conventions Typical Eloquent foreign key conventions will be used when performing the relationship's queries. If you would like to customize the keys of the relationship, you may pass them as the third and fourth arguments to the `hasManyThrough` method. The third argument is the name of the foreign key on the intermediate model. The fourth argument is the name of the foreign key on the final model. The fifth argument is the local key, while the sixth argument is the local key of the intermediate model: - class Country extends Model + class Project extends Model { public function posts() { return $this->hasManyThrough( - Post::class, - User::class, - 'country_id', // Foreign key on the users table... - 'user_id', // Foreign key on the posts table... - 'id', // Local key on the countries table... - 'id' // Local key on the users table... + Deployment::class, + Environment::class, + 'project_id', // Foreign key on the environments table... + 'environment_id', // Foreign key on the deployments table... + 'id', // Local key on the projects table... + 'id' // Local key on the environments table... ); } } From 719b84b35b0f351ae7e85711b001979ecd0f045c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 26 Nov 2020 10:03:05 -0600 Subject: [PATCH 186/274] wip --- eloquent-relationships.md | 87 +++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 06ad847380..87b21d5074 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -568,7 +568,7 @@ If you have defined a many-to-many relationship that uses a custom pivot model, ## Polymorphic Relationships -A polymorphic relationship allows the target model to belong to more than one type of model using a single association. +A polymorphic relationship allows the child model to belong to more than one type of model using a single association. For example, imagine you are building an application that allows users to share blog posts and videos. a `Comment` model might belong to both the `Post` and `Video` models. ### One To One (Polymorphic) @@ -576,7 +576,7 @@ A polymorphic relationship allows the target model to belong to more than one ty #### Table Structure -A one-to-one polymorphic relation is similar to a simple one-to-one relation; however, the target model can belong to more than one type of model on a single association. For example, a blog `Post` and a `User` may share a polymorphic relation to an `Image` model. Using a one-to-one polymorphic relation allows you to have a single list of unique images that are used for both blog posts and user accounts. First, let's examine the table structure: +A one-to-one polymorphic relation is similar to a typical one-to-one relation; however, the child model can belong to more than one type of model using a single association. For example, a blog `Post` and a `User` may share a polymorphic relation to an `Image` model. Using a one-to-one polymorphic relation allows you to have a single list of unique images that are used for both blog posts and user accounts. First, let's examine the table structure: posts id - integer @@ -592,7 +592,7 @@ A one-to-one polymorphic relation is similar to a simple one-to-one relation; ho imageable_id - integer imageable_type - string -Take note of the `imageable_id` and `imageable_type` columns on the `images` table. The `imageable_id` column will contain the ID value of the post or user, while the `imageable_type` column will contain the class name of the parent model. The `imageable_type` column is used by Eloquent to determine which "type" of parent model to return when accessing the `imageable` relation. +Note the `imageable_id` and `imageable_type` columns on the `images` table. The `imageable_id` column will contain the ID value of the post or user, while the `imageable_type` column will contain the class name of the parent model. The `imageable_type` column is used by Eloquent to determine which "type" of parent model to return when accessing the `imageable` relation. #### Model Structure @@ -608,7 +608,7 @@ Next, let's examine the model definitions needed to build this relationship: class Image extends Model { /** - * Get the owning imageable model. + * Get the parent imageable model (user or post). */ public function imageable() { @@ -623,7 +623,7 @@ Next, let's examine the model definitions needed to build this relationship: */ public function image() { - return $this->morphOne('App\Models\Image', 'imageable'); + return $this->morphOne(Image::class, 'imageable'); } } @@ -634,26 +634,35 @@ Next, let's examine the model definitions needed to build this relationship: */ public function image() { - return $this->morphOne('App\Models\Image', 'imageable'); + return $this->morphOne(Image::class, 'imageable'); } } #### Retrieving The Relationship -Once your database table and models are defined, you may access the relationships via your models. For example, to retrieve the image for a post, we can use the `image` dynamic property: +Once your database table and models are defined, you may access the relationships via your models. For example, to retrieve the image for a post, we can access the `image` dynamic relationship property: - $post = App\Models\Post::find(1); + use App\Models\Post; + + $post = Post::find(1); $image = $post->image; -You may also retrieve the parent from the polymorphic model by accessing the name of the method that performs the call to `morphTo`. In our case, that is the `imageable` method on the `Image` model. So, we will access that method as a dynamic property: +You may retrieve the parent of the polymorphic model by accessing the name of the method that performs the call to `morphTo`. In this case, that is the `imageable` method on the `Image` model. So, we will access that method as a dynamic relationship property: - $image = App\Models\Image::find(1); + use App\Models\Image; + + $image = Image::find(1); $imageable = $image->imageable; -The `imageable` relation on the `Image` model will return either a `Post` or `User` instance, depending on which type of model owns the image. If you need to specify custom `type` and `id` columns for the `morphTo` relation, always ensure you pass the relationship name (which should exactly match the method name) as the first parameter: +The `imageable` relation on the `Image` model will return either a `Post` or `User` instance, depending on which type of model owns the image. + + +#### Key Conventions + +If necessary, you may specify the name of the "id" and "type" columns utilized by your polymorphic child model. If you do so, ensure that you always pass the name of the relationship as the first argument to the `morphTo` method. Typically, this value should match the method name, so you may use PHP's `__FUNCTION__` constant: /** * Get the model that the image belongs to. @@ -669,7 +678,7 @@ The `imageable` relation on the `Image` model will return either a `Post` or `Us #### Table Structure -A one-to-many polymorphic relation is similar to a simple one-to-many relation; however, the target model can belong to more than one type of model on a single association. For example, imagine users of your application can "comment" on both posts and videos. Using polymorphic relationships, you may use a single `comments` table for both of these scenarios. First, let's examine the table structure required to build this relationship: +A one-to-many polymorphic relation is similar to a typical one-to-many relation; however, the child model can belong to more than one type of model using a single association. For example, imagine users of your application can "comment" on posts and videos. Using polymorphic relationships, you may use a single `comments` table to contain comments for both posts and videos. First, let's examine the table structure required to build this relationship: posts id - integer @@ -701,7 +710,7 @@ Next, let's examine the model definitions needed to build this relationship: class Comment extends Model { /** - * Get the owning commentable model. + * Get the parent commentable model (post or video). */ public function commentable() { @@ -716,7 +725,7 @@ Next, let's examine the model definitions needed to build this relationship: */ public function comments() { - return $this->morphMany('App\Models\Comment', 'commentable'); + return $this->morphMany(Comment::class, 'commentable'); } } @@ -727,28 +736,32 @@ Next, let's examine the model definitions needed to build this relationship: */ public function comments() { - return $this->morphMany('App\Models\Comment', 'commentable'); + return $this->morphMany(Comment::class, 'commentable'); } } #### Retrieving The Relationship -Once your database table and models are defined, you may access the relationships via your models. For example, to access all of the comments for a post, we can use the `comments` dynamic property: +Once your database table and models are defined, you may access the relationships via your model's dynamic relationship properties. For example, to access all of the comments for a post, we can use the `comments` dynamic property: - $post = App\Models\Post::find(1); + use App\Models\Post; + + $post = Post::find(1); foreach ($post->comments as $comment) { // } -You may also retrieve the owner of a polymorphic relation from the polymorphic model by accessing the name of the method that performs the call to `morphTo`. In our case, that is the `commentable` method on the `Comment` model. So, we will access that method as a dynamic property: +You may also retrieve the parent of a polymorphic child model by accessing the name of the method that performs the call to `morphTo`. In this case, that is the `commentable` method on the `Comment` model. So, we will access that method as a dynamic relationship property: - $comment = App\Models\Comment::find(1); + use App\Models\Comment; + + $comment = Comment::find(1); $commentable = $comment->commentable; -The `commentable` relation on the `Comment` model will return either a `Post` or `Video` instance, depending on which type of model owns the comment. +The `commentable` relation on the `Comment` model will return either a `Post` or `Video` instance, depending on which type of model is the comment's parent. ### Many To Many (Polymorphic) @@ -756,7 +769,7 @@ The `commentable` relation on the `Comment` model will return either a `Post` or #### Table Structure -Many-to-many polymorphic relations are slightly more complicated than `morphOne` and `morphMany` relationships. For example, a blog `Post` and `Video` model could share a polymorphic relation to a `Tag` model. Using a many-to-many polymorphic relation allows you to have a single list of unique tags that are shared across blog posts and videos. First, let's examine the table structure: +Many-to-many polymorphic relations are slightly more complicated than `morphOne` and `morphMany` relationships. For example, a blog `Post` and `Video` model could share a polymorphic relation to a `Tag` model. Using a many-to-many polymorphic relation in this situation would allow your application to have a single list of unique tags that are shared across blog posts and videos. First, let's examine the table structure required to build this relationship: posts id - integer @@ -775,10 +788,14 @@ Many-to-many polymorphic relations are slightly more complicated than `morphOne` taggable_id - integer taggable_type - string +> {tip} Before diving into polymorphic many-to-many relationships, you may benefit from reading the documentation on typical [many-to-many relationships](#many-to-many). + #### Model Structure -Next, we're ready to define the relationships on the model. The `Post` and `Video` models will both have a `tags` method that calls the `morphToMany` method on the base Eloquent class: +Next, we're ready to define the relationships on the models. The `Post` and `Video` models will both have a `tags` method that calls the `morphToMany` method provided by the base Eloquent model class. + +The `morphToMany` method accepts the name of the related model as well as the "relationship name". Based on the name we assigned to our intermediate table name and the keys it contains, we are referring to the relationship as "taggable": morphToMany('App\Models\Tag', 'taggable'); + return $this->morphToMany(Tag::class, 'taggable'); } } #### Defining The Inverse Of The Relationship -Next, on the `Tag` model, you should define a method for each of its related models. So, for this example, we will define a `posts` method and a `videos` method: +Next, on the `Tag` model, you should define a method for each of its related models. So, for this example, we will define a `posts` method and a `videos` method. Both of these methods should return the result of the `morphedByMany` method. + +The `morphedByMany` method accepts the name of the related model as well as the "relationship name". Based on the name we assigned to our intermediate table name and the keys it contains, we are referring to the relationship as "taggable": morphedByMany('App\Models\Post', 'taggable'); + return $this->morphedByMany(Post::class, 'taggable'); } /** @@ -823,24 +842,32 @@ Next, on the `Tag` model, you should define a method for each of its related mod */ public function videos() { - return $this->morphedByMany('App\Models\Video', 'taggable'); + return $this->morphedByMany(Video::class, 'taggable'); } } #### Retrieving The Relationship -Once your database table and models are defined, you may access the relationships via your models. For example, to access all of the tags for a post, you can use the `tags` dynamic property: +Once your database table and models are defined, you may access the relationships via your models. For example, to access all of the tags for a post, you can use the `tags` dynamic relationship property: - $post = App\Models\Post::find(1); + use App\Models\Post; + + $post = Post::find(1); foreach ($post->tags as $tag) { // } -You may also retrieve the owner of a polymorphic relation from the polymorphic model by accessing the name of the method that performs the call to `morphedByMany`. In our case, that is the `posts` or `videos` methods on the `Tag` model. So, you will access those methods as dynamic properties: +You may retrieve the parent of a polymorphic relation from the polymorphic child model by accessing the name of the method that performs the call to `morphedByMany`. In this case, that is the `posts` or `videos` methods on the `Tag` model. So, you may access those methods as dynamic relationship properties: + + use App\Models\Tag; - $tag = App\Models\Tag::find(1); + $tag = Tag::find(1); + + foreach ($tag->posts as $post) { + // + } foreach ($tag->videos as $video) { // From e9516810fdba62534d1935fed9ba8a9a21a87e7d Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 26 Nov 2020 10:24:47 -0600 Subject: [PATCH 187/274] wip --- eloquent-relationships.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 87b21d5074..67699beff7 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -876,20 +876,20 @@ You may retrieve the parent of a polymorphic relation from the polymorphic child ### Custom Polymorphic Types -By default, Laravel will use the fully qualified class name to store the type of the related model. For instance, given the one-to-many example above where a `Comment` may belong to a `Post` or a `Video`, the default `commentable_type` would be either `App\Models\Post` or `App\Models\Video`, respectively. However, you may wish to decouple your database from your application's internal structure. In that case, you may define a "morph map" to instruct Eloquent to use a custom name for each model instead of the class name: +By default, Laravel will use the fully qualified class name to store the "type" of the related model. For instance, given the one-to-many relationship example above where a `Comment` model may belong to a `Post` or a `Video` model, the default `commentable_type` would be either `App\Models\Post` or `App\Models\Video`, respectively. However, you may wish to decouple these values from your application's internal structure. + +For example, instead of using the model names as the "type", we may use simple strings such as `post` and `video`. By doing so, the polymorphic "type" column values in our database will remain valid even if the models are renamed: use Illuminate\Database\Eloquent\Relations\Relation; Relation::morphMap([ - 'posts' => 'App\Models\Post', - 'videos' => 'App\Models\Video', + 'post' => 'App\Models\Post', + 'video' => 'App\Models\Video', ]); -You may register the `morphMap` in the `boot` function of your `AppServiceProvider` or create a separate service provider if you wish. - -> {note} When adding a "morph map" to your existing application, every morphable `*_type` column value in your database that still contains a fully-qualified class will need to be converted to its "map" name. +You may register the `morphMap` in the `boot` function of your `App\Providers\AppServiceProvider` class or create a separate service provider if you wish. -You may determine the morph alias of a given model at runtime using the `getMorphClass` method. Conversely, you may determine the fully-qualified class name associated with a morph alias using the `Relation::getMorphedModel` method: +You may determine the morph alias of a given model at runtime using the model's `getMorphClass` method. Conversely, you may determine the fully-qualified class name associated with a morph alias using the `Relation::getMorphedModel` method: use Illuminate\Database\Eloquent\Relations\Relation; @@ -897,6 +897,8 @@ You may determine the morph alias of a given model at runtime using the `getMorp $class = Relation::getMorphedModel($alias); +> {note} When adding a "morph map" to your existing application, every morphable `*_type` column value in your database that still contains a fully-qualified class will need to be converted to its "map" name. + ### Dynamic Relationships From e14b860616365772c187f9e9b81f59a97fc04a64 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 26 Nov 2020 10:57:44 -0600 Subject: [PATCH 188/274] wip --- eloquent-relationships.md | 134 ++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 55 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 67699beff7..409c2a742d 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -21,7 +21,7 @@ - [Relationship Methods Vs. Dynamic Properties](#relationship-methods-vs-dynamic-properties) - [Querying Relationship Existence](#querying-relationship-existence) - [Querying Relationship Absence](#querying-relationship-absence) - - [Querying Polymorphic Relationships](#querying-polymorphic-relationships) + - [Querying `morphTo` Relationships](#querying-morph-to-relationships) - [Aggregating Related Models](#aggregating-related-models) - [Counting Related Models On Polymorphic Relationships](#counting-related-models-on-polymorphic-relationships) - [Eager Loading](#eager-loading) @@ -902,7 +902,9 @@ You may determine the morph alias of a given model at runtime using the model's ### Dynamic Relationships -You may use the `resolveRelationUsing` method to define relations between Eloquent models at runtime. While not typically recommended for normal application development, this may occasionally be useful when developing Laravel packages: +You may use the `resolveRelationUsing` method to define relations between Eloquent models at runtime. While not typically recommended for normal application development, this may occasionally be useful when developing Laravel packages. + +The `resolveRelationshipUsing` method accepts the desired relationship name as its first argument. The second argument passed to the method should be a closure that accepts the model instance and returns a valid Eloquent relationship definition. Typically, you should configure dynamic relationships within the boot method of a [service provider](/docs/{{version}}/providers): use App\Models\Order; use App\Models\Customer; @@ -916,9 +918,9 @@ You may use the `resolveRelationUsing` method to define relations between Eloque ## Querying Relations -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. +Since all Eloquent relationships are defined via methods, you may call those methods to obtain an instance of the relationship without actually executing a query to load the related models. 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 query against your database. -For example, imagine a blog system in which a `User` model has many associated `Post` models: +For example, imagine a blog application in which a `User` model has many associated `Post` models: hasMany('App\Models\Post'); + return $this->hasMany(Post::class); } } You may query the `posts` relationship and add additional constraints to the relationship like so: - $user = App\Models\User::find(1); + use App\Models\User; + + $user = User::find(1); $user->posts()->where('active', 1)->get(); -You are able to use any of the [query builder](/docs/{{version}}/queries) methods on the relationship, so be sure to explore the query builder documentation to learn about all of the methods that are available to you. +You are able to use any of the Laravel [query builder's](/docs/{{version}}/queries) methods on the relationship, so be sure to explore the query builder documentation to learn about all of the methods that are available to you. #### Chaining `orWhere` Clauses After Relationships @@ -955,10 +959,15 @@ As demonstrated in the example above, you are free to add additional constraints ->orWhere('votes', '>=', 100) ->get(); - // select * from posts - // where user_id = ? and active = 1 or votes >= 100 +The example above will generate the following SQL. As you can see, the `or` clause instructs the query to return _any_ use with greater than 100 votes. The query is no longer constrained to a specific user: + +```sql +select * +from posts +where user_id = ? and active = 1 or votes >= 100 +``` -In most situations, you likely intend to use [constraint groups](/docs/{{version}}/queries#parameter-grouping) to logically group the conditional checks between parentheses: +In most situations, you should use [logical groups](/docs/{{version}}/queries#logical-grouping) to group the conditional checks between parentheses: use Illuminate\Database\Eloquent\Builder; @@ -969,123 +978,138 @@ In most situations, you likely intend to use [constraint groups](/docs/{{version }) ->get(); - // select * from posts - // where user_id = ? and (active = 1 or votes >= 100) +The example above will produce the following SQL. Note that the logical grouping has properly grouped the constraints and the query remains constrained to a specific user: + +```sql +select * +from posts +where user_id = ? and (active = 1 or votes >= 100) +``` ### Relationship Methods Vs. Dynamic Properties If you do not need to add additional constraints to an Eloquent relationship query, you may access the relationship as if it were a property. For example, continuing to use our `User` and `Post` example models, we may access all of a user's posts like so: - $user = App\Models\User::find(1); + use App\Models\User; + + $user = User::find(1); foreach ($user->posts as $post) { // } -Dynamic properties are "lazy loading", meaning they will only load their relationship data when you actually access them. Because of this, developers often use [eager loading](#eager-loading) to pre-load relationships they know will be accessed after loading the model. Eager loading provides a significant reduction in SQL queries that must be executed to load a model's relations. +Dynamic relationship properties perform "lazy loading", meaning they will only load their relationship data when you actually access them. Because of this, developers often use [eager loading](#eager-loading) to pre-load relationships they know will be accessed after loading the model. Eager loading provides a significant reduction in SQL queries that must be executed to load a model's relations. ### Querying Relationship Existence -When accessing the records for a model, you may wish to limit your results based on the existence of a relationship. For example, imagine you want to retrieve all blog posts that have at least one comment. To do so, you may pass the name of the relationship to the `has` and `orHas` methods: +When retrieving model records, you may wish to limit your results based on the existence of a relationship. For example, imagine you want to retrieve all blog posts that have at least one comment. To do so, you may pass the name of the relationship to the `has` and `orHas` methods: + + use App\Models\Post; // Retrieve all posts that have at least one comment... - $posts = App\Models\Post::has('comments')->get(); + $posts = Post::has('comments')->get(); -You may also specify an operator and count to further customize the query: +You may also specify an operator and count value to further customize the query: // Retrieve all posts that have three or more comments... - $posts = App\Models\Post::has('comments', '>=', 3)->get(); + $posts = Post::has('comments', '>=', 3)->get(); -Nested `has` statements may also be constructed using "dot" notation. For example, you may retrieve all posts that have at least one comment and vote: +Nested `has` statements may be constructed using "dot" notation. For example, you may retrieve all posts that have at least one comment that has at least one image: - // Retrieve posts that have at least one comment with votes... - $posts = App\Models\Post::has('comments.votes')->get(); + // Retrieve posts that have at least one comment with images... + $posts = Post::has('comments.images')->get(); -If you need even more power, you may use the `whereHas` and `orWhereHas` methods to put "where" conditions on your `has` queries. These methods allow you to add customized constraints to a relationship constraint, such as checking the content of a comment: +If you need even more power, you may use the `whereHas` and `orWhereHas` methods to define additional query constraints on your `has` queries, such as inspecting the content of a comment: use Illuminate\Database\Eloquent\Builder; - // Retrieve posts with at least one comment containing words like foo%... - $posts = App\Models\Post::whereHas('comments', function (Builder $query) { - $query->where('content', 'like', 'foo%'); + // Retrieve posts with at least one comment containing words like code%... + $posts = Post::whereHas('comments', function (Builder $query) { + $query->where('content', 'like', 'code%'); })->get(); - // Retrieve posts with at least ten comments containing words like foo%... - $posts = App\Models\Post::whereHas('comments', function (Builder $query) { - $query->where('content', 'like', 'foo%'); + // Retrieve posts with at least ten comments containing words like code%... + $posts = Post::whereHas('comments', function (Builder $query) { + $query->where('content', 'like', 'code%'); }, '>=', 10)->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` and `orDoesntHave` methods: +When retrieving model records, 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` and `orDoesntHave` methods: + + use App\Models\Post; - $posts = App\Models\Post::doesntHave('comments')->get(); + $posts = Post::doesntHave('comments')->get(); -If you need even more power, you may use the `whereDoesntHave` and `orWhereDoesntHave` methods to put "where" conditions on your `doesntHave` queries. These methods allow you to add customized constraints to a relationship constraint, such as checking the content of a comment: +If you need even more power, you may use the `whereDoesntHave` and `orWhereDoesntHave` methods to add additional query constraints to your `doesntHave` queries, such as inspecting the content of a comment: use Illuminate\Database\Eloquent\Builder; - $posts = App\Models\Post::whereDoesntHave('comments', function (Builder $query) { - $query->where('content', 'like', 'foo%'); + $posts = Post::whereDoesntHave('comments', function (Builder $query) { + $query->where('content', 'like', 'code%'); })->get(); -You may use "dot" notation to execute a query against a nested relationship. For example, the following query will retrieve all posts that do not have comments and posts that have comments from authors that are not banned: +You may use "dot" notation to execute a query against a nested relationship. For example, the following query will retrieve all posts that do not have comments; however, posts that have comments from authors that are not banned will be included in the results: use Illuminate\Database\Eloquent\Builder; - $posts = App\Models\Post::whereDoesntHave('comments.author', function (Builder $query) { + $posts = Post::whereDoesntHave('comments.author', function (Builder $query) { $query->where('banned', 0); })->get(); - -### Querying Polymorphic Relationships + +### Querying `morphTo` Relationships -To query the existence of `MorphTo` relationships, you may use the `whereHasMorph` method and its corresponding methods: +To query the existence of "morph to" relationships, you may use the `whereHasMorph` and `whereDoesntHaveMorph` methods. These methods accept the name of the relationship as their first argument. Next, the methods accept the names of the related models that you wish to include in the query. Finally, you may provide a closure which customizes the relationship query: + use App\Models\Comment; + use App\Models\Post; + use App\Models\Video; use Illuminate\Database\Eloquent\Builder; - // Retrieve comments associated to posts or videos with a title like foo%... - $comments = App\Models\Comment::whereHasMorph( + // Retrieve comments associated to posts or videos with a title like code%... + $comments = Comment::whereHasMorph( 'commentable', - ['App\Models\Post', 'App\Models\Video'], + [Post::class, Video::class], function (Builder $query) { - $query->where('title', 'like', 'foo%'); + $query->where('title', 'like', 'code%'); } )->get(); - // Retrieve comments associated to posts with a title not like foo%... - $comments = App\Models\Comment::whereDoesntHaveMorph( + // Retrieve comments associated to posts with a title not like code%... + $comments = Comment::whereDoesntHaveMorph( 'commentable', - 'App\Models\Post', + Post::class function (Builder $query) { - $query->where('title', 'like', 'foo%'); + $query->where('title', 'like', 'code%'); } )->get(); -You may use the `$type` parameter to add different constraints depending on the related model: +You may occasionally need to add query constraints based on the "type" of the related polymorphic model. The closure passed to the `whereHasMorph` method may receive a `$type` value as its second argument. This argument allows you to inspect the "type" of the query that is being built: use Illuminate\Database\Eloquent\Builder; - $comments = App\Models\Comment::whereHasMorph( + $comments = Comment::whereHasMorph( 'commentable', - ['App\Models\Post', 'App\Models\Video'], + [Post::class, Video::class], function (Builder $query, $type) { - $query->where('title', 'like', 'foo%'); + $column = $type === Post::class ? 'content' : 'title'; - if ($type === 'App\Models\Post') { - $query->orWhere('content', 'like', 'foo%'); - } + $query->where($column, 'like', 'code%'); } )->get(); -Instead of passing an array of possible polymorphic models, you may provide `*` as a wildcard and let Laravel retrieve all the possible polymorphic types from the database. Laravel will execute an additional query in order to perform this operation: + +#### Querying All Related Models + +Instead of passing an array of possible polymorphic models, you may provide `*` as a wildcard value. This will instruct Laravel to retrieve all of the possible polymorphic types from the database. Laravel will execute an additional query in order to perform this operation: use Illuminate\Database\Eloquent\Builder; - $comments = App\Models\Comment::whereHasMorph('commentable', '*', function (Builder $query) { + $comments = Comment::whereHasMorph('commentable', '*', function (Builder $query) { $query->where('title', 'like', 'foo%'); })->get(); From df1a5ee682b9f913cd15e6e783c09dc8589d44c4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 26 Nov 2020 11:16:33 -0600 Subject: [PATCH 189/274] wip --- eloquent-relationships.md | 93 ++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 409c2a742d..8eb8459599 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -22,8 +22,10 @@ - [Querying Relationship Existence](#querying-relationship-existence) - [Querying Relationship Absence](#querying-relationship-absence) - [Querying `morphTo` Relationships](#querying-morph-to-relationships) - - [Aggregating Related Models](#aggregating-related-models) - - [Counting Related Models On Polymorphic Relationships](#counting-related-models-on-polymorphic-relationships) +- [Aggregating Related Models](#aggregating-related-models) + - [Counting Related Models](#counting-related-models) + - [Other Aggregate Functions](#other-aggregate-functions) + - [Counting Related Models On `morphTo` Relationships](#counting-related-models-on-morph-to-relationships) - [Eager Loading](#eager-loading) - [Constraining Eager Loads](#constraining-eager-loads) - [Lazy Eager Loading](#lazy-eager-loading) @@ -1114,25 +1116,27 @@ Instead of passing an array of possible polymorphic models, you may provide `*` })->get(); -### Aggregating Related Models +## Aggregating Related Models -#### Counting Related Models +### Counting Related Models -If you want to count the number of results from a relationship without actually loading them you may use the `withCount` method, which will place a `{relation}_count` column on your resulting models. For example: +Sometimes you may want to count the number of related models for a given relationship without actually loading the models. To accomplish this, you may use the `withCount` method. The `withCount` method which will place a `{relation}_count` attribute on the resulting models: - $posts = App\Models\Post::withCount('comments')->get(); + use App\Models\Post; + + $posts = Post::withCount('comments')->get(); foreach ($posts as $post) { echo $post->comments_count; } -You may add the "counts" for multiple relations as well as add constraints to the queries: +By passing an array to the `withCount` method, you may add the "counts" for multiple relations as well as add additional constraints to the queries: use Illuminate\Database\Eloquent\Builder; - $posts = App\Models\Post::withCount(['votes', 'comments' => function (Builder $query) { - $query->where('content', 'like', 'foo%'); + $posts = Post::withCount(['votes', 'comments' => function (Builder $query) { + $query->where('content', 'like', 'code%'); }])->get(); echo $posts[0]->votes_count; @@ -1142,7 +1146,7 @@ You may also alias the relationship count result, allowing multiple counts on th use Illuminate\Database\Eloquent\Builder; - $posts = App\Models\Post::withCount([ + $posts = Post::withCount([ 'comments', 'comments as pending_comments_count' => function (Builder $query) { $query->where('approved', false); @@ -1150,72 +1154,81 @@ You may also alias the relationship count result, allowing multiple counts on th ])->get(); echo $posts[0]->comments_count; - echo $posts[0]->pending_comments_count; -If you're combining `withCount` with a `select` statement, ensure that you call `withCount` after the `select` method: - - $posts = App\Models\Post::select(['title', 'body'])->withCount('comments')->get(); + +#### Deferred Count Loading - echo $posts[0]->title; - echo $posts[0]->body; - echo $posts[0]->comments_count; - -In addition, using the `loadCount` method, you may load a relationship count after the parent model has already been retrieved: +Using the `loadCount` method, you may load a relationship count after the parent model has already been retrieved: - $book = App\Models\Book::first(); + $book = Book::first(); $book->loadCount('genres'); -If you need to set additional query constraints on the eager loading query, you may pass an array keyed by the relationships you wish to load. The array values should be closure instances which receive the query builder instance: +If you need to set additional query constraints on the count query, you may pass an array keyed by the relationships you wish to count. The array values should be closures which receive the query builder instance: $book->loadCount(['reviews' => function ($query) { $query->where('rating', 5); }]) -#### Other Aggregate Functions + +#### Relationship Counting & Custom Select Statements + +If you're combining `withCount` with a `select` statement, ensure that you call `withCount` after the `select` method: + + $posts = Post::select(['title', 'body']) + ->withCount('comments') + ->get(); -In addition to the `withCount` method, Eloquent provides `withMin`, `withMax`, `withAvg`, and `withSum`. These methods will place a `{relation}_{function}_{column}` column on your resulting models. For example: + +### Other Aggregate Functions - $posts = App\Models\Post::withSum('comments', 'votes')->get(); +In addition to the `withCount` method, Eloquent provides `withMin`, `withMax`, `withAvg`, and `withSum` methods. These methods will place a `{relation}_{function}_{column}` attribute on your resulting models: + + use App\Models\Post; + + $posts = Post::withSum('comments', 'votes')->get(); foreach ($posts as $post) { echo $post->comments_sum_votes; } -These additional aggregate operations may also be performed on Eloquent models that have already been retrieved: +Like the `loadCount` method, deferred versions of these methods are also available. These additional aggregate operations may be performed on Eloquent models that have already been retrieved: - $post = App\Models\Post::first(); + $post = Post::first(); $post->loadSum('comments', 'votes'); - -### Counting Related Models On Polymorphic Relationships + +### Counting Related Models On `morphTo` Relationships -If you would like to eager load a `morphTo` relationship, as well as nested relationship counts on the various entities that may be returned by that relationship, you may use the `with` method in combination with the `morphTo` relationship's `morphWithCount` method. +If you would like to eager load a "morph to" relationship, as well as related model counts for the various entities that may be returned by that relationship, you may utilize the `with` method in combination with the `morphTo` relationship's `morphWithCount` method. -In this example, let's assume `Photo` and `Post` models may create `ActivityFeed` models. Additionally, let's assume that `Photo` models are associated with `Tag` models, and `Post` models are associated with `Comment` models. +In this example, let's assume that `Photo` and `Post` models may create `ActivityFeed` models. We will assume the `ActivityFeed` model defines a "morph to" relationship named `parentable` that allows us to retrieve the parent `Photo` or `Post` model for a given `ActivityFeed` instance. Additionally, let's assume that `Photo` models "have many" `Tag` models and `Post` models "have many" `Comment` models. -Using these model definitions and relationships, we may retrieve `ActivityFeed` model instances and eager load all `parentable` models and their respective nested relationship counts: +Now, let's imagine we want to retrieve `ActivityFeed` instances and eager load the `parentable` parent models for each `ActivityFeed` instance. In addition, we want to retrieve the number of tags that are associated with each parent photo and the number of comments that are associated with each parent post: use Illuminate\Database\Eloquent\Relations\MorphTo; - $activities = ActivityFeed::query() - ->with(['parentable' => function (MorphTo $morphTo) { + $activities = ActivityFeed::with([ + 'parentable' => function (MorphTo $morphTo) { $morphTo->morphWithCount([ Photo::class => ['tags'], Post::class => ['comments'], ]); }])->get(); -In addition, you may use the `loadMorphCount` method to eager load all nested relationship counts on the various entities of the polymorphic relation if the `ActivityFeed` models have already been retrieved: + +#### Deferred Count Loading - $activities = ActivityFeed::with('parentable') - ->get() - ->loadMorphCount('parentable', [ - Photo::class => ['tags'], - Post::class => ['comments'], - ]); +Let's assume we have already retrieved a set of `ActivityFeed` models and now we would like to load the nested relationship counts for the various `parentable` models associated with the activity feeds. You may use the `loadMorphCount` method to accomplish this: + + $activities = ActivityFeed::with('parentable')->get(); + + $activities->loadMorphCount('parentable', [ + Photo::class => ['tags'], + Post::class => ['comments'], + ]); ## Eager Loading From 127de74cc399d0bc6ce0cbd42ec3517425d9dff8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 27 Nov 2020 09:32:10 -0600 Subject: [PATCH 190/274] formatting --- cache.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cache.md b/cache.md index a586848e86..31401a126b 100644 --- a/cache.md +++ b/cache.md @@ -409,7 +409,7 @@ We just need to implement each of these methods using a MongoDB connection. For ### Registering The Driver -To register the custom cache driver with Laravel, we will use the `extend` method on the `Cache` facade. The call to `Cache::extend` could be done in the `boot` method of the default `App\Providers\AppServiceProvider` that ships with fresh Laravel applications, or you may create your own service provider to house the extension - just don't forget to register the provider in the `config/app.php` provider array: +To register the custom cache driver with Laravel, we will use the `extend` method on the `Cache` facade. Since other service providers may attempt to read cached values within their `boot` method, we will register our custom driver within a `booting` callback. By using the `booting` callback, we can ensure that the custom driver is registered just before the `boot` method is called on our application's service providers but after the `register` method is called on all of the service providers. We will register our `booting` callback within the `register` method of our application's `App\Providers\AppServiceProvider` class: app->booting(function () { + Cache::extend('mongo', function ($app) { + return Cache::repository(new MongoStore); + }); + }); } /** @@ -438,9 +442,7 @@ To register the custom cache driver with Laravel, we will use the `extend` metho */ public function boot() { - Cache::extend('mongo', function ($app) { - return Cache::repository(new MongoStore); - }); + // } } From d1db6793f2f2271c1d659d6861ba3068acbd69c8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 27 Nov 2020 09:55:59 -0600 Subject: [PATCH 191/274] make note about runtime config --- database.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database.md b/database.md index 3890fe2fe5..f24fe39ead 100644 --- a/database.md +++ b/database.md @@ -206,7 +206,7 @@ Please refer to the MySQL manual for [a list of all statements](https://dev.mysq ### Using Multiple Database Connections -If your application defines multiple connections in your `config/database.php` configuration file, you may access each connection via the `connection` method provided by the `DB` facade. The connection name passed to the `connection` method should correspond to one of the connections listed in your `config/database.php` configuration file: +If your application defines multiple connections in your `config/database.php` configuration file, you may access each connection via the `connection` method provided by the `DB` facade. The connection name passed to the `connection` method should correspond to one of the connections listed in your `config/database.php` configuration file or configured at runtime using the `config` helper: use Illuminate\Support\Facades\DB; From db14dd15ef67bdac84b71ce484dcb3e69743f5c7 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 27 Nov 2020 11:44:21 -0600 Subject: [PATCH 192/274] wip --- eloquent-relationships.md | 244 +++++++++++++++++++------------------- 1 file changed, 120 insertions(+), 124 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 8eb8459599..992d3b568e 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -21,11 +21,11 @@ - [Relationship Methods Vs. Dynamic Properties](#relationship-methods-vs-dynamic-properties) - [Querying Relationship Existence](#querying-relationship-existence) - [Querying Relationship Absence](#querying-relationship-absence) - - [Querying `morphTo` Relationships](#querying-morph-to-relationships) + - [Querying Morph To Relationships](#querying-morph-to-relationships) - [Aggregating Related Models](#aggregating-related-models) - [Counting Related Models](#counting-related-models) - [Other Aggregate Functions](#other-aggregate-functions) - - [Counting Related Models On `morphTo` Relationships](#counting-related-models-on-morph-to-relationships) + - [Counting Related Models On Morph To Relationships](#counting-related-models-on-morph-to-relationships) - [Eager Loading](#eager-loading) - [Constraining Eager Loads](#constraining-eager-loads) - [Lazy Eager Loading](#lazy-eager-loading) @@ -239,6 +239,41 @@ If your parent model does not use `id` as its primary key, or you wish to find t return $this->belongsTo(Post::class, 'foreign_key', 'owner_key'); } + +#### Default Models + +The `belongsTo`, `hasOne`, `hasOneThrough`, and `morphOne` relationships allow you to define a default model that will be returned if the given relationship is `null`. This pattern is often referred to as the [Null Object pattern](https://en.wikipedia.org/wiki/Null_Object_pattern) and can help remove conditional checks in your code. In the following example, the `user` relation will return an empty `App\Models\User` model if no user is attached to the `Post` model: + + /** + * Get the author of the post. + */ + public function user() + { + return $this->belongsTo(User::class)->withDefault(); + } + +To populate the default model with attributes, you may pass an array or closure to the `withDefault` method: + + /** + * Get the author of the post. + */ + public function user() + { + return $this->belongsTo(User::class)->withDefault([ + 'name' => 'Guest Author', + ]); + } + + /** + * Get the author of the post. + */ + public function user() + { + return $this->belongsTo(User::class)->withDefault(function ($user, $post) { + $user->name = 'Guest Author'; + }); + } + ### Has One Through @@ -1063,7 +1098,7 @@ You may use "dot" notation to execute a query against a nested relationship. For })->get(); -### Querying `morphTo` Relationships +### Querying Morph To Relationships To query the existence of "morph to" relationships, you may use the `whereHasMorph` and `whereDoesntHaveMorph` methods. These methods accept the name of the relationship as their first argument. Next, the methods accept the names of the related models that you wish to include in the query. Finally, you may provide a closure which customizes the relationship query: @@ -1200,7 +1235,7 @@ Like the `loadCount` method, deferred versions of these methods are also availab $post->loadSum('comments', 'votes'); -### Counting Related Models On `morphTo` Relationships +### Counting Related Models On Morph To Relationships If you would like to eager load a "morph to" relationship, as well as related model counts for the various entities that may be returned by that relationship, you may utilize the `with` method in combination with the `morphTo` relationship's `morphWithCount` method. @@ -1233,7 +1268,7 @@ Let's assume we have already retrieved a set of `ActivityFeed` models and now we ## Eager Loading -When accessing Eloquent relationships as properties, the relationship data is "lazy loaded". This means the relationship data is not actually loaded until you first access the property. However, Eloquent can "eager load" relationships at the time you query the parent model. Eager loading alleviates the N + 1 query problem. To illustrate the N + 1 query problem, consider a `Book` model that is related to `Author`: +When accessing Eloquent relationships as properties, the related models are "lazy loaded". This means the relationship data is not actually loaded until you first access the property. However, Eloquent can "eager load" relationships at the time you query the parent model. Eager loading alleviates the "N + 1" query problem. To illustrate the N + 1 query problem, consider a `Book` model that "belongs to" to an `Author` model: belongsTo('App\Models\Author'); + return $this->belongsTo(Author::class); } } Now, let's retrieve all books and their authors: - $books = App\Models\Book::all(); + use App\Models\Book; + + $books = Book::all(); foreach ($books as $book) { echo $book->author->name; } -This loop will execute 1 query to retrieve all of the books on the table, then another query for each book to retrieve the author. So, if we have 25 books, the code above would run 26 queries: 1 for the original book, and 25 additional queries to retrieve the author of each book. +This loop will execute one query to retrieve all of the books within the database table, then another query for each book in order to retrieve the book's author. So, if we have 25 books, the code above would run 26 queries: one for the original book, and 25 additional queries to retrieve the author of each book. -Thankfully, we can use eager loading to reduce this operation to just 2 queries. When querying, you may specify which relationships should be eager loaded using the `with` method: +Thankfully, we can use eager loading to reduce this operation to just two queries. When building a query, you may specify which relationships should be eager loaded using the `with` method: - $books = App\Models\Book::with('author')->get(); + $books = Book::with('author')->get(); foreach ($books as $book) { echo $book->author->name; } -For this operation, only two queries will be executed: +For this operation, only two queries will be executed - one query to retrieve all of the books and one query to retrieve all of the authors for all of the books: - select * from books +```sql +select * from books - select * from authors where id in (1, 2, 3, 4, 5, ...) +select * from authors where id in (1, 2, 3, 4, 5, ...) +``` #### Eager Loading Multiple Relationships -Sometimes you may need to eager load several different relationships in a single operation. To do so, just pass additional arguments to the `with` method: +Sometimes you may need to eager load several different relationships. To do so, just pass an array of relationships to the `with` method: - $books = App\Models\Book::with(['author', 'publisher'])->get(); + $books = Book::with(['author', 'publisher'])->get(); #### Nested Eager Loading -To eager load nested relationships, you may use "dot" syntax. For example, let's eager load all of the book's authors and all of the author's personal contacts in one Eloquent statement: +To eager a relationship's relationships, you may use "dot" syntax. For example, let's eager load all of the book's authors and all of the author's personal contacts: - $books = App\Models\Book::with('author.contacts')->get(); + $books = Book::with('author.contacts')->get(); #### Nested Eager Loading `morphTo` Relationships @@ -1330,7 +1369,7 @@ Using these model definitions and relationships, we may retrieve `ActivityFeed` You may not always need every column from the relationships you are retrieving. For this reason, Eloquent allows you to specify which columns of the relationship you would like to retrieve: - $books = App\Models\Book::with('author:id,name')->get(); + $books = Book::with('author:id,name')->get(); > {note} When using this feature, you should always include the `id` column and any relevant foreign key columns in the list of columns you wish to retrieve. @@ -1359,35 +1398,37 @@ Sometimes you might want to always load some relationships when retrieving a mod */ public function author() { - return $this->belongsTo('App\Models\Author'); + return $this->belongsTo(Author::class); } } If you would like to remove an item from the `$with` property for a single query, you may use the `without` method: - $books = App\Models\Book::without('author')->get(); + $books = Book::without('author')->get(); ### Constraining Eager Loads -Sometimes you may wish to eager load a relationship, but also specify additional query conditions for the eager loading query. Here's an example: +Sometimes you may wish to eager load a relationship but also specify additional query conditions for the eager loading query. You can accomplish this by passing an array of relationships to the `with` method where the array key is a relationship name and the array value is a closure that adds additional constraints to the eager loading query: - $users = App\Models\User::with(['posts' => function ($query) { - $query->where('title', 'like', '%first%'); + use App\Models\User; + + $users = User::with(['posts' => function ($query) { + $query->where('title', 'like', '%code%'); }])->get(); -In this example, Eloquent will only eager load posts where the post's `title` column contains the word `first`. You may call other [query builder](/docs/{{version}}/queries) methods to further customize the eager loading operation: +In this example, Eloquent will only eager load posts where the post's `title` column contains the word `code`. You may call other [query builder](/docs/{{version}}/queries) methods to further customize the eager loading operation: - $users = App\Models\User::with(['posts' => function ($query) { + $users = User::with(['posts' => function ($query) { $query->orderBy('created_at', 'desc'); }])->get(); > {note} The `limit` and `take` query builder methods may not be used when constraining eager loads. -#### Constraining Eager Loading Of `MorphTo` Relationships +#### Constraining Eager Loading Of `morphTo` Relationships -If you are eager loading a `MorphTo` relationship, Eloquent will run multiple queries to fetch each different type of related model. You may target conditions to each of these queries using the `MorphTo` relation's `constrain` method: +If you are eager loading a `morphTo` relationship, Eloquent will run multiple queries to fetch each type of related model. You may add additional constraints to each of these queries using the `MorphTo` relation's `constrain` method: use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\MorphTo; @@ -1410,7 +1451,9 @@ In this example, Eloquent will only eager load posts that have not been hidden a Sometimes you may need to eager load a relationship after the parent model has already been retrieved. For example, this may be useful if you need to dynamically decide whether to load related models: - $books = App\Models\Book::all(); + use App\Models\Book; + + $books = Book::all(); if ($someCondition) { $books->load('author', 'publisher'); @@ -1424,15 +1467,7 @@ If you need to set additional query constraints on the eager loading query, you To load a relationship only when it has not already been loaded, use the `loadMissing` method: - public function format(Book $book) - { - $book->loadMissing('author'); - - return [ - 'name' => $book->name, - 'author' => $book->author->name, - ]; - } + $book->loadMissing('author'); #### Nested Lazy Eager Loading & `morphTo` @@ -1472,25 +1507,28 @@ Using these model definitions and relationships, we may retrieve `ActivityFeed` ## Inserting & Updating Related Models -### The Save Method +### The `save` Method -Eloquent provides convenient methods for adding new models to relationships. For example, perhaps you need to insert a new `Comment` for a `Post` model. Instead of manually setting the `post_id` attribute on the `Comment`, you may insert the `Comment` directly from the relationship's `save` method: +Eloquent provides convenient methods for adding new models to relationships. For example, perhaps you need to add a new comment to a post. Instead of manually setting the `post_id` attribute on the `Comment` model you may insert the comment using the relationship's `save` method: + + use App\Models\Comment; + use App\Models\Post; - $comment = new App\Models\Comment(['message' => 'A new comment.']); + $comment = new Comment(['message' => 'A new comment.']); - $post = App\Models\Post::find(1); + $post = Post::find(1); $post->comments()->save($comment); -Notice that we did not access the `comments` relationship as a dynamic property. Instead, we called the `comments` method to obtain an instance of the relationship. The `save` method will automatically add the appropriate `post_id` value to the new `Comment` model. +Note that we did not access the `comments` relationship as a dynamic property. Instead, we called the `comments` method to obtain an instance of the relationship. The `save` method will automatically add the appropriate `post_id` value to the new `Comment` model. If you need to save multiple related models, you may use the `saveMany` method: - $post = App\Models\Post::find(1); + $post = Post::find(1); $post->comments()->saveMany([ - new App\Models\Comment(['message' => 'A new comment.']), - new App\Models\Comment(['message' => 'Another comment.']), + new Comment(['message' => 'A new comment.']), + new Comment(['message' => 'Another new comment.']), ]); The `save` and `saveMany` methods will not add the new models to any in-memory relationships that are already loaded onto the parent model. If you plan on accessing the relationship after using the `save` or `saveMany` methods, you may wish to use the `refresh` method to reload the model and its relationships: @@ -1505,9 +1543,9 @@ The `save` and `saveMany` methods will not add the new models to any in-memory r #### Recursively Saving Models & Relationships -If you would like to `save` your model and all of its associated relationships, you may use the `push` method: +If you would like to `save` your model and all of its associated relationships, you may use the `push` method. In this example, the `Post` model will be saved as well as its comments and the comment's authors: - $post = App\Models\Post::find(1); + $post = Post::find(1); $post->comments[0]->message = 'Message'; $post->comments[0]->author->name = 'Author Name'; @@ -1515,94 +1553,61 @@ If you would like to `save` your model and all of its associated relationships, $post->push(); -### The Create Method +### The `create` Method -In addition to the `save` and `saveMany` methods, you may also use the `create` method, which accepts an array of attributes, creates a model, and inserts it into the database. Again, the difference between `save` and `create` is that `save` accepts a full Eloquent model instance while `create` accepts a plain PHP `array`: +In addition to the `save` and `saveMany` methods, you may also use the `create` method, which accepts an array of attributes, creates a model, and inserts it into the database. The difference between `save` and `create` is that `save` accepts a full Eloquent model instance while `create` accepts a plain PHP `array`. The newly created model will be returned by the `create` method: - $post = App\Models\Post::find(1); + use App\Models\Post; + + $post = Post::find(1); $comment = $post->comments()->create([ 'message' => 'A new comment.', ]); -> {tip} Before using the `create` method, be sure to review the documentation on attribute [mass assignment](/docs/{{version}}/eloquent#mass-assignment). - You may use the `createMany` method to create multiple related models: - $post = App\Models\Post::find(1); + $post = Post::find(1); $post->comments()->createMany([ - [ - 'message' => 'A new comment.', - ], - [ - 'message' => 'Another new comment.', - ], + ['message' => 'A new comment.'], + ['message' => 'Another new comment.'], ]); -You may also use the `findOrNew`, `firstOrNew`, `firstOrCreate` and `updateOrCreate` methods to [create and update models on relationships](https://laravel.com/docs/{{version}}/eloquent#other-creation-methods). +You may also use the `findOrNew`, `firstOrNew`, `firstOrCreate`, and `updateOrCreate` methods to [create and update models on relationships](https://laravel.com/docs/{{version}}/eloquent#other-creation-methods). + +> {tip} Before using the `create` method, be sure to review the [mass assignment](/docs/{{version}}/eloquent#mass-assignment) documentation. ### Belongs To Relationships -When updating a `belongsTo` relationship, you may use the `associate` method. This method will set the foreign key on the child model: +If you would like to assign a child model to a new parent model, you may use the `associate` method. In this example, the `User` model defines a `belongsTo` relationship to the `Account` model. This `associate` method will set the foreign key on the child model: + + use App\Models\Account; - $account = App\Models\Account::find(10); + $account = Account::find(10); $user->account()->associate($account); $user->save(); -When removing a `belongsTo` relationship, you may use the `dissociate` method. This method will set the relationship's foreign key to `null`: +To remove a parent model from a child model, you may use the `dissociate` method. This method will set the relationship's foreign key to `null`: $user->account()->dissociate(); $user->save(); - -#### Default Models - -The `belongsTo`, `hasOne`, `hasOneThrough`, and `morphOne` relationships allow you to define a default model that will be returned if the given relationship is `null`. This pattern is often referred to as the [Null Object pattern](https://en.wikipedia.org/wiki/Null_Object_pattern) and can help remove conditional checks in your code. In the following example, the `user` relation will return an empty `App\Models\User` model if no `user` is attached to the post: - - /** - * Get the author of the post. - */ - public function user() - { - return $this->belongsTo('App\Models\User')->withDefault(); - } - -To populate the default model with attributes, you may pass an array or closure to the `withDefault` method: - - /** - * Get the author of the post. - */ - public function user() - { - return $this->belongsTo('App\Models\User')->withDefault([ - 'name' => 'Guest Author', - ]); - } - - /** - * Get the author of the post. - */ - public function user() - { - return $this->belongsTo('App\Models\User')->withDefault(function ($user, $post) { - $user->name = 'Guest Author'; - }); - } - ### Many To Many Relationships #### Attaching / Detaching -Eloquent also provides a few additional helper methods to make working with related models more convenient. For example, let's imagine a user can have many roles and a role can have many users. To attach a role to a user by inserting a record in the intermediate table that joins the models, use the `attach` method: +Eloquent also provides methods to make working with many-to-many relationships more convenient. For example, let's imagine a user can have many roles and a role can have many users. You may use the `attach` method to attach a role to a user by inserting a record in the relationship's intermediate table: + + use App\Models\User; - $user = App\Models\User::find(1); + $user = User::find(1); $user->roles()->attach($roleId); @@ -1620,7 +1625,7 @@ Sometimes it may be necessary to remove a role from a user. To remove a many-to- For convenience, `attach` and `detach` also accept arrays of IDs as input: - $user = App\Models\User::find(1); + $user = User::find(1); $user->roles()->detach([1, 2, 3]); @@ -1640,37 +1645,34 @@ You may also pass additional intermediate table values with the IDs: $user->roles()->sync([1 => ['expires' => true], 2, 3]); -If you do not want to detach existing IDs, you may use the `syncWithoutDetaching` method: +If you do not want to detach existing IDs that are missing from the given array, you may use the `syncWithoutDetaching` method: $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: +The many-to-many relationship also provides a `toggle` method which "toggles" the attachment status of the given related model 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: + +#### Updating A Record On The Intermediate Table - App\Models\User::find(1)->roles()->save($role, ['expires' => $expires]); +If you need to update an existing row in your relationship's intermediate table, you may use the `updateExistingPivot` method. This method accepts the intermediate record foreign key and an array of attributes to update: - -#### Updating A Record On A Pivot Table - -If you need to update an existing row in your pivot table, you may use `updateExistingPivot` method. This method accepts the pivot record foreign key and an array of attributes to update: - - $user = App\Models\User::find(1); + $user = User::find(1); - $user->roles()->updateExistingPivot($roleId, $attributes); + $user->roles()->updateExistingPivot($roleId, [ + 'active' => false, + ]); ## Touching Parent Timestamps -When a model `belongsTo` or `belongsToMany` another model, such as a `Comment` which belongs to a `Post`, it is sometimes helpful to update the parent's timestamp when the child model is updated. For example, when a `Comment` model is updated, you may want to automatically "touch" the `updated_at` timestamp of the owning `Post`. Eloquent makes it easy. Just add a `touches` property containing the names of the relationships to the child model: +When a model defines a `belongsTo` or `belongsToMany` relationship to another model, such as a `Comment` which belongs to a `Post`, it is sometimes helpful to update the parent's timestamp when the child model is updated. + +For example, when a `Comment` model is updated, you may want to automatically "touch" the `updated_at` timestamp of the owning `Post` so that it is set to the current date and time. To accomplish this, you may add a `touches` property to your child model containing the names of the relationships that should have their `updated_at` timestamps updated when the child model is updated: belongsTo('App\Models\Post'); + return $this->belongsTo(Post::class); } } -Now, when you update a `Comment`, the owning `Post` will have its `updated_at` column updated as well, making it more convenient to know when to invalidate a cache of the `Post` model: - - $comment = App\Models\Comment::find(1); - - $comment->text = 'Edit to this comment!'; - - $comment->save(); +> {note} Parent model timestamps will only be updated if the child model is updated using Eloquent's `save` method. From 10012205eed2cd6f8b8700d4fbdd6768d9f0bcf4 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 27 Nov 2020 13:31:03 -0600 Subject: [PATCH 193/274] wip --- eloquent-relationships.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 992d3b568e..868c76ef0e 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -613,7 +613,7 @@ A polymorphic relationship allows the child model to belong to more than one typ #### Table Structure -A one-to-one polymorphic relation is similar to a typical one-to-one relation; however, the child model can belong to more than one type of model using a single association. For example, a blog `Post` and a `User` may share a polymorphic relation to an `Image` model. Using a one-to-one polymorphic relation allows you to have a single list of unique images that are used for both blog posts and user accounts. First, let's examine the table structure: +A one-to-one polymorphic relation is similar to a typical one-to-one relation; however, the child model can belong to more than one type of model using a single association. For example, a blog `Post` and a `User` may share a polymorphic relation to an `Image` model. Using a one-to-one polymorphic relation allows you to have a single table of unique images that may be associated with posts and users. First, let's examine the table structure: posts id - integer @@ -629,7 +629,7 @@ A one-to-one polymorphic relation is similar to a typical one-to-one relation; h imageable_id - integer imageable_type - string -Note the `imageable_id` and `imageable_type` columns on the `images` table. The `imageable_id` column will contain the ID value of the post or user, while the `imageable_type` column will contain the class name of the parent model. The `imageable_type` column is used by Eloquent to determine which "type" of parent model to return when accessing the `imageable` relation. +Note the `imageable_id` and `imageable_type` columns on the `images` table. The `imageable_id` column will contain the ID value of the post or user, while the `imageable_type` column will contain the class name of the parent model. The `imageable_type` column is used by Eloquent to determine which "type" of parent model to return when accessing the `imageable` relation. In this case, the column would contain either `App\Models\Post` or `App\Models\User`. #### Model Structure From 63bc16eeb6c2d603eeea6d9b27e3e0c8f4143be8 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 27 Nov 2020 13:36:13 -0600 Subject: [PATCH 194/274] wip --- eloquent-relationships.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/eloquent-relationships.md b/eloquent-relationships.md index 868c76ef0e..4244ba7435 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -279,7 +279,7 @@ To populate the default model with attributes, you may pass an array or closure The "has-one-through" relationship defines a one-to-one relationship with another model. However, this relationship indicates that the declaring model can be matched with one instance of another model by proceeding _through_ a third model. -For example, in a vehicle repair shop application, each `Mechanic` model may be associated with one `Car` model, and each `Car` model may be associated with one `Owner` model. While the `Mechanic` and the `Owner` have no direct connection, the `Mechanic` can access the `Owner` _through_ the `Car` model. Let's look at the tables necessary to define this relationship: +For example, in a vehicle repair shop application, each `Mechanic` model may be associated with one `Car` model, and each `Car` model may be associated with one `Owner` model. While the mechanic and the owner have no direct relationship within the database, the mechanic can access the owner _through_ the `Car` model. Let's look at the tables necessary to define this relationship: mechanics id - integer @@ -790,7 +790,7 @@ Once your database table and models are defined, you may access the relationship // } -You may also retrieve the parent of a polymorphic child model by accessing the name of the method that performs the call to `morphTo`. In this case, that is the `commentable` method on the `Comment` model. So, we will access that method as a dynamic relationship property: +You may also retrieve the parent of a polymorphic child model by accessing the name of the method that performs the call to `morphTo`. In this case, that is the `commentable` method on the `Comment` model. So, we will access that method as a dynamic relationship property in order to access the comment's parent model: use App\Models\Comment; @@ -806,7 +806,7 @@ The `commentable` relation on the `Comment` model will return either a `Post` or #### Table Structure -Many-to-many polymorphic relations are slightly more complicated than `morphOne` and `morphMany` relationships. For example, a blog `Post` and `Video` model could share a polymorphic relation to a `Tag` model. Using a many-to-many polymorphic relation in this situation would allow your application to have a single list of unique tags that are shared across blog posts and videos. First, let's examine the table structure required to build this relationship: +Many-to-many polymorphic relations are slightly more complicated than "morph one" and "morph many" relationships. For example, a `Post` model and `Video` model could share a polymorphic relation to a `Tag` model. Using a many-to-many polymorphic relation in this situation would allow your application to have a single table of unique tags that may be associated with posts or videos. First, let's examine the table structure required to build this relationship: posts id - integer @@ -830,9 +830,9 @@ Many-to-many polymorphic relations are slightly more complicated than `morphOne` #### Model Structure -Next, we're ready to define the relationships on the models. The `Post` and `Video` models will both have a `tags` method that calls the `morphToMany` method provided by the base Eloquent model class. +Next, we're ready to define the relationships on the models. The `Post` and `Video` models will both contain a `tags` method that calls the `morphToMany` method provided by the base Eloquent model class. -The `morphToMany` method accepts the name of the related model as well as the "relationship name". Based on the name we assigned to our intermediate table name and the keys it contains, we are referring to the relationship as "taggable": +The `morphToMany` method accepts the name of the related model as well as the "relationship name". Based on the name we assigned to our intermediate table name and the keys it contains, we will refer to the relationship as "taggable": #### Defining The Inverse Of The Relationship -Next, on the `Tag` model, you should define a method for each of its related models. So, for this example, we will define a `posts` method and a `videos` method. Both of these methods should return the result of the `morphedByMany` method. +Next, on the `Tag` model, you should define a method for each of its possible parent models. So, in this example, we will define a `posts` method and a `videos` method. Both of these methods should return the result of the `morphedByMany` method. -The `morphedByMany` method accepts the name of the related model as well as the "relationship name". Based on the name we assigned to our intermediate table name and the keys it contains, we are referring to the relationship as "taggable": +The `morphedByMany` method accepts the name of the related model as well as the "relationship name". Based on the name we assigned to our intermediate table name and the keys it contains, we will refer to the relationship as "taggable": #### Retrieving The Relationship -Once your database table and models are defined, you may access the relationships via your models. For example, to access all of the tags for a post, you can use the `tags` dynamic relationship property: +Once your database table and models are defined, you may access the relationships via your models. For example, to access all of the tags for a post, you may use the `tags` dynamic relationship property: use App\Models\Post; @@ -896,7 +896,7 @@ Once your database table and models are defined, you may access the relationship // } -You may retrieve the parent of a polymorphic relation from the polymorphic child model by accessing the name of the method that performs the call to `morphedByMany`. In this case, that is the `posts` or `videos` methods on the `Tag` model. So, you may access those methods as dynamic relationship properties: +You may retrieve the parent of a polymorphic relation from the polymorphic child model by accessing the name of the method that performs the call to `morphedByMany`. In this case, that is the `posts` or `videos` methods on the `Tag` model: use App\Models\Tag; From f3836a6930219669b44fa493b6cac56e6268cf5f Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 27 Nov 2020 13:47:48 -0600 Subject: [PATCH 195/274] wip --- eloquent-collections.md | 46 ++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/eloquent-collections.md b/eloquent-collections.md index b06655367f..a617db79cd 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -7,35 +7,37 @@ ## Introduction -All multi-result sets returned by Eloquent are instances of the `Illuminate\Database\Eloquent\Collection` object, including results retrieved via the `get` method or accessed via a relationship. The Eloquent collection object extends the Laravel [base collection](/docs/{{version}}/collections), so it naturally inherits dozens of methods used to fluently work with the underlying array of Eloquent models. +All Eloquent methods that return more than one model result will return instances of the `Illuminate\Database\Eloquent\Collection` class, including results retrieved via the `get` method or accessed via a relationship. The Eloquent collection object extends Laravel's [base collection](/docs/{{version}}/collections), so it naturally inherits dozens of methods used to fluently work with the underlying array of Eloquent models. Be sure to review the Laravel collection documentation to learn all about these helpful methods! All collections also serve as iterators, allowing you to loop over them as if they were simple PHP arrays: - $users = App\Models\User::where('active', 1)->get(); + use App\Models\User; + + $users = User::where('active', 1)->get(); foreach ($users as $user) { echo $user->name; } -However, collections are much more powerful than arrays and expose a variety of map / reduce operations that may be chained using an intuitive interface. For example, let's remove all inactive models and gather the first name for each remaining user: - - $users = App\Models\User::all(); +However, as previously mentioned, collections are much more powerful than arrays and expose a variety of map / reduce operations that may be chained using an intuitive interface. For example, we may remove all inactive models and then gather the first name for each remaining user: - $names = $users->reject(function ($user) { + $names = User::all()->reject(function ($user) { return $user->active === false; - }) - ->map(function ($user) { + })->map(function ($user) { return $user->name; }); -> {note} While most Eloquent collection methods return a new instance of an Eloquent collection, the `pluck`, `keys`, `zip`, `collapse`, `flatten` and `flip` methods return a [base collection](/docs/{{version}}/collections) instance. Likewise, if a `map` operation returns a collection that does not contain any Eloquent models, it will be automatically cast to a base collection. + +#### Eloquent Collection Conversion + +While most Eloquent collection methods return a new instance of an Eloquent collection, the `collapse`, `flatten`, `flip`, `keys`, `pluck`, and `zip` methods return a [base collection](/docs/{{version}}/collections) instance. Likewise, if a `map` operation returns a collection that does not contain any Eloquent models, it will be converted to a base collection instance. ## Available Methods All Eloquent collections extend the base [Laravel collection](/docs/{{version}}/collections#available-methods) object; therefore, they inherit all of the powerful methods provided by the base collection class. -In addition, the `Illuminate\Database\Eloquent\Collection` class provides a superset of methods to aid with managing your model collections. Most methods return `Illuminate\Database\Eloquent\Collection` instances; however, some methods return a base `Illuminate\Support\Collection` instance. +In addition, the `Illuminate\Database\Eloquent\Collection` class provides a superset of methods to aid with managing your model collections. Most methods return `Illuminate\Database\Eloquent\Collection` instances; however, some methods, like `modelKeys`, return an `Illuminate\Support\Collection` instance.