diff --git a/artisan.md b/artisan.md index 768c94ec296..683c4b023cd 100644 --- a/artisan.md +++ b/artisan.md @@ -43,7 +43,7 @@ php artisan help migrate If you are using [Laravel Sail](/docs/{{version}}/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: ```shell -./sail artisan list +./vendor/bin/sail artisan list ``` @@ -60,7 +60,8 @@ All Laravel applications include Tinker by default. However, you may install Tin composer require laravel/tinker ``` -> {tip} Looking for a graphical UI for interacting with your Laravel application? Check out [Tinkerwell](https://tinkerwell.app)! +> **Note** +> Looking for a graphical UI for interacting with your Laravel application? Check out [Tinkerwell](https://tinkerwell.app)! #### Usage @@ -77,7 +78,8 @@ You can publish Tinker's configuration file using the `vendor:publish` command: php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider" ``` -> {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. +> **Warning** +> 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. #### Command Allow List @@ -154,7 +156,8 @@ Let's take a look at an example command. Note that we are able to request any de } } -> {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. +> **Note** +> 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. ### Closure Commands @@ -285,10 +288,10 @@ If you would like to define arguments or options to expect multiple input values 'mail: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 an array with `1` and `2` as its values: ```shell -php artisan mail:send foo bar +php artisan mail:send 1 2 ``` This `*` character can be combined with an optional argument definition to allow zero or more instances of an argument: @@ -300,7 +303,7 @@ This `*` character can be combined with an optional argument definition to allow When defining an option that expects multiple input values, each option value passed to the command should be prefixed with the option name: - 'mail:send {user} {--id=*}' + 'mail:send {--id=*}' Such a command may be invoked by passing multiple `--id` arguments: @@ -495,7 +498,8 @@ Sometimes, you may need more manual control over how a progress bar is advanced. $bar->finish(); -> {tip} For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/current/components/console/helpers/progressbar.html). +> **Note** +> For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/current/components/console/helpers/progressbar.html). ## Registering Commands diff --git a/authentication.md b/authentication.md index cc71a308214..25581e0c034 100644 --- a/authentication.md +++ b/authentication.md @@ -39,7 +39,8 @@ Providers define how users are retrieved from your persistent storage. Laravel s 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. -> {tip} Guards and providers should not be confused with "roles" and "permissions". To learn more about authorizing user actions via permissions, please refer to the [authorization](/docs/{{version}}/authorization) documentation. +> **Note** +> Guards and providers should not be confused with "roles" and "permissions". To learn more about authorizing user actions via permissions, please refer to the [authorization](/docs/{{version}}/authorization) documentation. ### Starter Kits @@ -79,7 +80,7 @@ _Laravel Breeze_ is a simple, minimal implementation of all of Laravel's authent _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 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. +_[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](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 @@ -109,12 +110,13 @@ If you are building a single-page application (SPA) that will be powered by a La Passport may be chosen when your application absolutely needs all of the features provided by the OAuth2 specification. -And, if you would like to get started quickly, we are pleased to recommend [Laravel Jetstream](https://jetstream.laravel.com) as a quick way to start a new Laravel application that already uses our preferred authentication stack of Laravel's built-in authentication services and Laravel Sanctum. +And, if you would like to get started quickly, we are pleased to recommend [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze) as a quick way to start a new Laravel application that already uses our preferred authentication stack of Laravel's built-in authentication services and Laravel Sanctum. ## Authentication Quickstart -> {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). +> **Warning** +> 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 @@ -123,7 +125,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](https://tailwindcss.com). Breeze also offers an [Inertia](https://inertiajs.com) based scaffolding option using Vue or React. -[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. +[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 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. ### Retrieving The Authenticated User @@ -171,7 +173,8 @@ To determine if the user making the incoming HTTP request is authenticated, you // The user is logged in... } -> {tip} Even though it is possible to determine if a user is authenticated using the `check` method, you will typically use a middleware to verify that the user is authenticated before allowing the user access to certain routes / controllers. To learn more about this, check out the documentation on [protecting routes](/docs/{{version}}/authentication#protecting-routes). +> **Note** +> Even though it is possible to determine if a user is authenticated using the `check` method, you will typically use a middleware to verify that the user is authenticated before allowing the user access to certain routes / controllers. To learn more about this, check out the documentation on [protecting routes](/docs/{{version}}/authentication#protecting-routes). ### Protecting Routes @@ -212,7 +215,8 @@ When attaching the `auth` middleware to a route, you may also specify which "gua 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 other routes in your application, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). +> **Note** +> If you would like to rate limit other routes in your application, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting). ## Manually Authenticating Users @@ -272,7 +276,19 @@ If you wish, you may also add extra query conditions to the authentication query // 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 table. +> **Warning** +> 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. + +The `attemptWhen` method, which receives a closure as its second argument, may be used to perform more extensive inspection of the potential user before actually authenticating the user. The closure receives the potential user and should return `true` or `false` to indicate if the user may be authenticated: + + if (Auth::attemptWhen([ + 'email' => $email, + 'password' => $password, + ], function ($user) { + return $user->isNotBanned(); + })) { + // Authentication was successful... + } #### Accessing Specific Guard Instances @@ -453,7 +469,8 @@ 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 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! +> **Note** +> 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/authorization.md b/authorization.md index 67c80938e67..3175bcd6bd3 100644 --- a/authorization.md +++ b/authorization.md @@ -38,7 +38,8 @@ You do not need to choose between exclusively using gates or exclusively using p ### 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. +> **Warning** +> 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 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. @@ -195,6 +196,33 @@ When using the `Gate::authorize` method, which throws an `AuthorizationException // The action is authorized... + +#### Customizing The HTTP Response Status + +When an action is denied via a Gate, a `403` HTTP response is returned; however, it can sometimes be useful to return an alternative HTTP status code. You may customize the HTTP status code returned for a failed authorization check using the `denyWithStatus` static constructor on the `Illuminate\Auth\Access\Response` class: + + use App\Models\User; + use Illuminate\Auth\Access\Response; + use Illuminate\Support\Facades\Gate; + + Gate::define('edit-settings', function (User $user) { + return $user->isAdmin + ? Response::allow() + : Response::denyWithStatus(404); + }); + +Because hiding resources via a `404` response is such a common pattern for web applications, the `denyAsNotFound` method is offered for convenience: + + use App\Models\User; + use Illuminate\Auth\Access\Response; + use Illuminate\Support\Facades\Gate; + + Gate::define('edit-settings', function (User $user) { + return $user->isAdmin + ? Response::allow() + : Response::denyAsNotFound(); + }); + ### Intercepting Gate Checks @@ -233,7 +261,7 @@ Gate::allowIf(fn ($user) => $user->isAdministrator()); Gate::denyIf(fn ($user) => $user->banned()); ``` -If the action is not authorized or if no user is currently authenticated, Laravel will automatically throw an `Illuminate\Auth\Access\AuthorizationException` exception. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler: +If the action is not authorized or if no user is currently authenticated, Laravel will automatically throw an `Illuminate\Auth\Access\AuthorizationException` exception. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler. ## Creating Policies @@ -308,7 +336,8 @@ If you would like to define your own policy discovery logic, you may register a // 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. +> **Warning** +> Any policies that are explicitly mapped in your `AuthServiceProvider` will take precedence over any potentially auto-discovered policies. ## Writing Policies @@ -346,7 +375,8 @@ You may continue to define additional methods on the policy as needed for the va 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. +> **Note** +> 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. ### Policy Responses @@ -389,6 +419,49 @@ When using the `Gate::authorize` method, which throws an `AuthorizationException // The action is authorized... + +#### Customizing The HTTP Response Status + +When an action is denied via a policy method, a `403` HTTP response is returned; however, it can sometimes be useful to return an alternative HTTP status code. You may customize the HTTP status code returned for a failed authorization check using the `denyWithStatus` static constructor on the `Illuminate\Auth\Access\Response` class: + + use App\Models\Post; + use App\Models\User; + use Illuminate\Auth\Access\Response; + + /** + * Determine if the given post can be updated by the user. + * + * @param \App\Models\User $user + * @param \App\Models\Post $post + * @return \Illuminate\Auth\Access\Response + */ + public function update(User $user, Post $post) + { + return $user->id === $post->user_id + ? Response::allow() + : Response::denyWithStatus(404); + } + +Because hiding resources via a `404` response is such a common pattern for web applications, the `denyAsNotFound` method is offered for convenience: + + use App\Models\Post; + use App\Models\User; + use Illuminate\Auth\Access\Response; + + /** + * Determine if the given post can be updated by the user. + * + * @param \App\Models\User $user + * @param \App\Models\Post $post + * @return \Illuminate\Auth\Access\Response + */ + public function update(User $user, Post $post) + { + return $user->id === $post->user_id + ? Response::allow() + : Response::denyAsNotFound(); + } + ### Methods Without Models @@ -455,7 +528,8 @@ For certain users, you may wish to authorize all actions within a given policy. 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. +> **Warning** +> 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. ## Authorizing Actions Using Policies @@ -622,7 +696,8 @@ The following controller methods will be mapped to their corresponding policy me | update | update | | destroy | delete | -> {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`. +> **Note** +> 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`. ### Via Middleware diff --git a/billing.md b/billing.md index d3662815dfa..ea147eadeee 100644 --- a/billing.md +++ b/billing.md @@ -31,7 +31,7 @@ - [Checking Subscription Status](#checking-subscription-status) - [Changing Prices](#changing-prices) - [Subscription Quantity](#subscription-quantity) - - [Multiprice Subscriptions](#multiprice-subscriptions) + - [Subscriptions With Multiple Products](#subscriptions-with-multiple-products) - [Metered Billing](#metered-billing) - [Subscription Taxes](#subscription-taxes) - [Subscription Anchor Date](#subscription-anchor-date) @@ -76,7 +76,8 @@ When upgrading to a new version of Cashier, it's important that you carefully review [the upgrade guide](https://github.com/laravel/cashier-stripe/blob/master/UPGRADE.md). -> {note} To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 13 utilizes Stripe API version `2020-08-27`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. +> **Warning** +> To prevent breaking changes, Cashier uses a fixed Stripe API version. Cashier 14 utilizes Stripe API version `2022-08-01`. The Stripe API version will be updated on minor releases in order to make use of new Stripe features and improvements. ## Installation @@ -87,7 +88,8 @@ First, install the Cashier package for Stripe using the Composer package manager composer require laravel/cashier ``` -> {note} To ensure Cashier properly handles all Stripe events, remember to [set up Cashier's webhook handling](#handling-stripe-webhooks). +> **Warning** +> To ensure Cashier properly handles all Stripe events, remember to [set up Cashier's webhook handling](#handling-stripe-webhooks). ### Database Migrations @@ -118,7 +120,8 @@ If you would like to prevent Cashier's migrations from running entirely, you may Cashier::ignoreMigrations(); } -> {note} Stripe recommends that any column used for storing Stripe identifiers should be case-sensitive. Therefore, you should ensure the column collation for the `stripe_id` column is set to `utf8_bin` when using MySQL. More information regarding this can be found in the [Stripe documentation](https://stripe.com/docs/upgrades#what-changes-does-stripe-consider-to-be-backwards-compatible). +> **Warning** +> Stripe recommends that any column used for storing Stripe identifiers should be case-sensitive. Therefore, you should ensure the column collation for the `stripe_id` column is set to `utf8_bin` when using MySQL. More information regarding this can be found in the [Stripe documentation](https://stripe.com/docs/upgrades#what-changes-does-stripe-consider-to-be-backwards-compatible). ## Configuration @@ -150,7 +153,8 @@ Cashier assumes your billable model will be the `App\Models\User` class that shi Cashier::useCustomerModel(User::class); } -> {note} If you're using a model other than Laravel's supplied `App\Models\User` model, you'll need to publish and alter the [Cashier migrations](#installation) provided to match your alternative model's table name. +> **Warning** +> If you're using a model other than Laravel's supplied `App\Models\User` model, you'll need to publish and alter the [Cashier migrations](#installation) provided to match your alternative model's table name. ### API Keys @@ -163,7 +167,8 @@ STRIPE_SECRET=your-stripe-secret STRIPE_WEBHOOK_SECRET=your-stripe-webhook-secret ``` -> {note} You should ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is defined in your application's `.env` file, as this variable is used to ensure that incoming webhooks are actually from Stripe. +> **Warning** +> You should ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is defined in your application's `.env` file, as this variable is used to ensure that incoming webhooks are actually from Stripe. ### Currency Configuration @@ -180,7 +185,8 @@ In addition to configuring Cashier's currency, you may also specify a locale to CASHIER_CURRENCY_LOCALE=nl_BE ``` -> {note} In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. +> **Warning** +> In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. ### Tax Configuration @@ -203,7 +209,8 @@ Once tax calculation has been enabled, any new subscriptions and any one-off inv For this feature to work properly, your customer's billing details, such as the customer's name, address, and tax ID, need to be synced to Stripe. You may use the [customer data synchronization](#syncing-customer-data-with-stripe) and [Tax ID](#tax-ids) methods offered by Cashier to accomplish this. -> {note} Unfortunately, for now, no tax is calculated for [single charges](#single-charges) or [single charge checkouts](#single-charge-checkouts). In addition, Stripe Tax is currently "invite-only" during its beta period. You can request access to Stripe Tax via the [Stripe Tax website](https://stripe.com/tax#request-access). +> **Warning** +> Unfortunately, for now, no tax is calculated for [single charges](#single-charges) or [single charge checkouts](#single-charge-checkouts). In addition, Stripe Tax is currently "invite-only" during its beta period. You can request access to Stripe Tax via the [Stripe Tax website](https://stripe.com/tax#request-access). ### Logging @@ -289,13 +296,13 @@ Stripe allows you to credit or debit a customer's "balance". Later, this balance $balance = $user->balance(); -To credit a customer's balance, you may provide a negative value to the `applyBalance` method. If you wish, you may also provide a description: +To credit a customer's balance, you may provide a value to the `creditBalance` method. If you wish, you may also provide a description: - $user->applyBalance(-500, 'Premium customer top-up.'); + $user->creditBalance(500, 'Premium customer top-up.'); -Providing a positive value to the `applyBalance` method will debit the customer's balance: +Providing a value to the `debitBalance` method will debit the customer's balance: - $user->applyBalance(300, 'Bad usage penalty.'); + $user->debitBalance(300, 'Bad usage penalty.'); The `applyBalance` method will create new customer balance transactions for the customer. You may retrieve these transaction records using the `balanceTransactions` method, which may be useful in order to provide a log of credits and debits for the customer to review: @@ -368,7 +375,7 @@ You may customize the columns used for syncing customer information to Stripe by return $this->company_name; } -Similarly, you may override the `stripeEmail`, `stripePhone`, and `stripeAddress` methods. These methods will sync information to their corresponding customer parameters when [updating the Stripe customer object](https://stripe.com/docs/api/customers/update). If you wish to take total control over the customer information sync process, you may override the `syncStripeCustomerDetails` method. +Similarly, you may override the `stripeEmail`, `stripePhone`, `stripeAddress`, and `stripePreferredLocales` methods. These methods will sync information to their corresponding customer parameters when [updating the Stripe customer object](https://stripe.com/docs/api/customers/update). If you wish to take total control over the customer information sync process, you may override the `syncStripeCustomerDetails` method. ### Billing Portal @@ -399,7 +406,7 @@ If you would like to generate the URL to the billing portal without generating a ### Storing Payment Methods -In order to create subscriptions or perform "one off" charges with Stripe, you will need to store a payment method and retrieve its identifier from Stripe. The approach used to accomplish this differs based on whether you plan to use the payment method for subscriptions or single charges, so we will examine both below. +In order to create subscriptions or perform "one-off" charges with Stripe, you will need to store a payment method and retrieve its identifier from Stripe. The approach used to accomplish this differs based on whether you plan to use the payment method for subscriptions or single charges, so we will examine both below. #### Payment Methods For Subscriptions @@ -465,7 +472,8 @@ cardButton.addEventListener('click', async (e) => { After the card has been verified by Stripe, you may pass the resulting `setupIntent.payment_method` identifier to your Laravel application, where it can be attached to the customer. The payment method can either be [added as a new payment method](#adding-payment-methods) or [used to update the default payment method](#updating-the-default-payment-method). You can also immediately use the payment method identifier to [create a new subscription](#creating-subscriptions). -> {tip} If you would like more information about Setup Intents and gathering customer payment details please [review this overview provided by Stripe](https://stripe.com/docs/payments/save-and-reuse#php). +> **Note** +> If you would like more information about Setup Intents and gathering customer payment details please [review this overview provided by Stripe](https://stripe.com/docs/payments/save-and-reuse#php). #### Payment Methods For Single Charges @@ -572,7 +580,8 @@ To sync your default payment method information with the customer's default paym $user->updateDefaultPaymentMethodFromStripe(); -> {note} The default payment method on a customer can only be used for invoicing and creating new subscriptions. Due to limitations imposed by Stripe, it may not be used for single charges. +> **Warning** +> The default payment method on a customer can only be used for invoicing and creating new subscriptions. Due to limitations imposed by Stripe, it may not be used for single charges. ### Adding Payment Methods @@ -581,7 +590,8 @@ To add a new payment method, you may call the `addPaymentMethod` method on the b $user->addPaymentMethod($paymentMethod); -> {tip} To learn how to retrieve payment method identifiers please review the [payment method storage documentation](#storing-payment-methods). +> **Note** +> To learn how to retrieve payment method identifiers please review the [payment method storage documentation](#storing-payment-methods). ### Deleting Payment Methods @@ -602,7 +612,8 @@ By default, this method will delete payment methods of the `card` type. To delet $user->deletePaymentMethods('sepa_debit'); -> {note} If a user has an active subscription, your application should not allow them to delete their default payment method. +> **Warning** +> If a user has an active subscription, your application should not allow them to delete their default payment method. ## Subscriptions @@ -628,7 +639,8 @@ The first argument passed to the `newSubscription` method should be the internal The `create` method, which accepts [a Stripe payment method identifier](#storing-payment-methods) or Stripe `PaymentMethod` object, will begin the subscription as well as update your database with the billable model's Stripe customer ID and other relevant billing information. -> {note} Passing a payment method identifier directly to the `create` subscription method will also automatically add it to the user's stored payment methods. +> **Warning** +> Passing a payment method identifier directly to the `create` subscription method will also automatically add it to the user's stored payment methods. #### Collecting Recurring Payments Via Invoice Emails @@ -734,7 +746,7 @@ You may also create subscriptions from the Stripe dashboard itself. When doing s In addition, you may only create one type of subscription via the Stripe dashboard. If your application offers multiple subscriptions that use different names, only one type of subscription may be added through the Stripe dashboard. -Finally, you should always make sure to only add one active subscription per type of subscription offered by your application. If customer has two `default` subscriptions, only the most recently added subscription will be used by Cashier even though both would be synced with your application's database. +Finally, you should always make sure to only add one active subscription per type of subscription offered by your application. If a customer has two `default` subscriptions, only the most recently added subscription will be used by Cashier even though both would be synced with your application's database. ### Checking Subscription Status @@ -803,7 +815,8 @@ The `recurring` method may be used to determine if the user is currently subscri // } -> {note} If a user has two subscriptions with the same name, the most recent subscription will always be returned by the `subscription` method. For example, a user might have two subscription records named `default`; however, one of the subscriptions may be an old, expired subscription, while the other is the current, active subscription. The most recent subscription will always be returned while older subscriptions are kept in the database for historical review. +> **Warning** +> If a user has two subscriptions with the same name, the most recent subscription will always be returned by the `subscription` method. For example, a user might have two subscription records named `default`; however, one of the subscriptions may be an old, expired subscription, while the other is the current, active subscription. The most recent subscription will always be returned while older subscriptions are kept in the database for historical review. #### Canceled Subscription Status @@ -863,7 +876,8 @@ If you would like the subscription to still be considered active when it's in a Cashier::keepPastDueSubscriptionsActive(); } -> {note} When a subscription is in an `incomplete` state it cannot be changed until the payment is confirmed. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in an `incomplete` state. +> **Warning** +> When a subscription is in an `incomplete` state it cannot be changed until the payment is confirmed. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in an `incomplete` state. #### Subscription Scopes @@ -924,7 +938,8 @@ By default, Stripe prorates charges when swapping between prices. The `noProrate For more information on subscription proration, consult the [Stripe documentation](https://stripe.com/docs/billing/subscriptions/prorations). -> {note} Executing the `noProrate` method before the `swapAndInvoice` method will have no effect on proration. An invoice will always be issued. +> **Warning** +> Executing the `noProrate` method before the `swapAndInvoice` method will have no effect on proration. An invoice will always be issued. ### Subscription Quantity @@ -955,19 +970,19 @@ The `noProrate` method may be used to update the subscription's quantity without For more information on subscription quantities, consult the [Stripe documentation](https://stripe.com/docs/subscriptions/quantities). - -#### Multiprice Subscription Quantities + +#### Quantities For Subscriptions With Multiple Products -If your subscription is a [multiprice subscription](#multiprice-subscriptions), you should pass the name of the price whose quantity you wish to increment or decrement as the second argument to the increment / decrement methods: +If your subscription is a [subscription with multiple products](#subscriptions-with-multiple-products), you should pass the ID of the price whose quantity you wish to increment or decrement as the second argument to the increment / decrement methods: $user->subscription('default')->incrementQuantity(1, 'price_chat'); - -### Multiprice Subscriptions + +### Subscriptions With Multiple Products -[Multiprice subscriptions](https://stripe.com/docs/billing/subscriptions/multiple-products) allow you to assign multiple billing prices to a single subscription. For example, imagine you are building a customer service "helpdesk" application that has a base subscription price of $10 per month but offers a live chat add-on price for an additional $15 per month. Multiprice subscription information is stored in Cashier's `subscription_items` database table. +[Subscription with multiple products](https://stripe.com/docs/billing/subscriptions/multiple-products) allow you to assign multiple billing products to a single subscription. For example, imagine you are building a customer service "helpdesk" application that has a base subscription price of $10 per month but offers a live chat add-on product for an additional $15 per month. Information for subscriptions with multiple products is stored in Cashier's `subscription_items` database table. -You may specify multiple prices for a given subscription by passing an array of prices as the second argument to the `newSubscription` method: +You may specify multiple products for a given subscription by passing an array of prices as the second argument to the `newSubscription` method: use Illuminate\Http\Request; @@ -1008,12 +1023,13 @@ You may remove prices from subscriptions using the `removePrice` method: $user->subscription('default')->removePrice('price_chat'); -> {note} You may not remove the last price on a subscription. Instead, you should simply cancel the subscription. +> **Warning** +> You may not remove the last price on a subscription. Instead, you should simply cancel the subscription. #### Swapping Prices -You may also change the prices attached to a multiprice subscription. For example, imagine a customer has a `price_basic` subscription with a `price_chat` add-on price and you want to upgrade the customer from the `price_basic` to the `price_pro` price: +You may also change the prices attached to a subscription with multiple products. For example, imagine a customer has a `price_basic` subscription with a `price_chat` add-on product and you want to upgrade the customer from the `price_basic` to the `price_pro` price: use App\Models\User; @@ -1043,7 +1059,7 @@ If you want to swap a single price on a subscription, you may do so using the `s #### Proration -By default, Stripe will prorate charges when adding or removing prices from a multiprice subscription. If you would like to make a price adjustment without proration, you should chain the `noProrate` method onto your price operation: +By default, Stripe will prorate charges when adding or removing prices from a subscription with multiple products. If you would like to make a price adjustment without proration, you should chain the `noProrate` method onto your price operation: $user->subscription('default')->noProrate()->removePrice('price_chat'); @@ -1060,7 +1076,8 @@ If you would like to update quantities on individual subscription prices, you ma $user->subscription('default')->updateQuantity(10, 'price_chat'); -> {note} When a subscription has multiple prices the `stripe_price` and `quantity` attributes on the `Subscription` model will be `null`. To access the individual price attributes, you should use the `items` relationship available on the `Subscription` model. +> **Warning** +> When a subscription has multiple prices the `stripe_price` and `quantity` attributes on the `Subscription` model will be `null`. To access the individual price attributes, you should use the `items` relationship available on the `Subscription` model. #### Subscription Items @@ -1166,7 +1183,8 @@ For a full reference of all usage data returned and how to use Stripe's cursor b ### Subscription Taxes -> {note} Instead of calculating Tax Rates manually, you can [automatically calculate taxes using Stripe Tax](#tax-configuration) +> **Warning** +> Instead of calculating Tax Rates manually, you can [automatically calculate taxes using Stripe Tax](#tax-configuration) To specify the tax rates a user pays on a subscription, you should implement the `taxRates` method on your billable model and return an array containing the Stripe tax rate IDs. You can define these tax rates in [your Stripe dashboard](https://dashboard.stripe.com/test/tax-rates): @@ -1182,7 +1200,7 @@ To specify the tax rates a user pays on a subscription, you should implement the The `taxRates` method enables you to apply a tax rate on a customer-by-customer basis, which may be helpful for a user base that spans multiple countries and tax rates. -If you're offering multiprice subscriptions, you may define different tax rates for each price by implementing a `priceTaxRates` method on your billable model: +If you're offering subscriptions with multiple products, you may define different tax rates for each price by implementing a `priceTaxRates` method on your billable model: /** * The tax rates that should apply to the customer's subscriptions. @@ -1196,7 +1214,8 @@ If you're offering multiprice subscriptions, you may define different tax rates ]; } -> {note} The `taxRates` method only applies to subscription charges. If you use Cashier to make "one off" charges, you will need to manually specify the tax rate at that time. +> **Warning** +> The `taxRates` method only applies to subscription charges. If you use Cashier to make "one-off" charges, you will need to manually specify the tax rate at that time. #### Syncing Tax Rates @@ -1205,7 +1224,7 @@ When changing the hard-coded tax rate IDs returned by the `taxRates` method, the $user->subscription('default')->syncTaxRates(); -This will also sync any multiprice subscription item tax rates. If your application is offering multiprice subscriptions, you should ensure that your billable model implements the `priceTaxRates` method [discussed above](#subscription-taxes). +This will also sync any item tax rates for a subscription with multiple products. If your application is offering subscriptions with multiple products, you should ensure that your billable model implements the `priceTaxRates` method [discussed above](#subscription-taxes). #### Tax Exemption @@ -1220,7 +1239,8 @@ Cashier also offers the `isNotTaxExempt`, `isTaxExempt`, and `reverseChargeAppli $user->isNotTaxExempt(); $user->reverseChargeApplies(); -> {note} These methods are also available on any `Laravel\Cashier\Invoice` object. However, when invoked on an `Invoice` object, the methods will determine the exemption status at the time the invoice was created. +> **Warning** +> These methods are also available on any `Laravel\Cashier\Invoice` object. However, when invoked on an `Invoice` object, the methods will determine the exemption status at the time the invoice was created. ### Subscription Anchor Date @@ -1301,7 +1321,8 @@ If you would like to offer trial periods to your customers while still collectin This method will set the trial period ending date on the subscription record within the database and instruct Stripe to not begin billing the customer until after this date. When using the `trialDays` method, Cashier will overwrite any default trial period configured for the price in Stripe. -> {note} If the customer's subscription is not canceled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date. +> **Warning** +> If the customer's subscription is not canceled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date. The `trialUntil` method allows you to provide a `DateTime` instance that specifies when the trial period should end: @@ -1325,6 +1346,16 @@ You may use the `endTrial` method to immediately end a subscription trial: $user->subscription('default')->endTrial(); +To determine if an existing trial has expired, you may use the `hasExpiredTrial` methods: + + if ($user->hasExpiredTrial('default')) { + // + } + + if ($user->subscription('default')->hasExpiredTrial()) { + // + } + #### Defining Trial Days In Stripe / Cashier @@ -1342,7 +1373,8 @@ If you would like to offer trial periods without collecting the user's payment m 'trial_ends_at' => now()->addDays(10), ]); -> {note} Be sure to add a [date cast](/docs/{{version}}/eloquent-mutators##date-casting) for the `trial_ends_at` attribute within your billable model's class definition. +> **Warning** +> Be sure to add a [date cast](/docs/{{version}}/eloquent-mutators##date-casting) for the `trial_ends_at` attribute within your billable model's class definition. Cashier refers to this type of trial as a "generic trial", since it is not attached to any existing subscription. The `onTrial` method on the billable model instance will return `true` if the current date is not past the value of `trial_ends_at`: @@ -1390,7 +1422,8 @@ The `extendTrial` method allows you to extend the trial period of a subscription ## Handling Stripe Webhooks -> {tip} You may use [the Stripe CLI](https://stripe.com/docs/stripe-cli) to help test webhooks during local development. +> **Note** +> You may use [the Stripe CLI](https://stripe.com/docs/stripe-cli) to help test webhooks during local development. Stripe can notify your application of a variety of events via webhooks. By default, a route that points to Cashier's webhook controller is automatically registered by the Cashier service provider. This controller will handle all incoming webhook requests. @@ -1429,7 +1462,8 @@ After creation, the webhook will be immediately active. If you wish to create th php artisan cashier:webhook --disabled ``` -> {note} Make sure you protect incoming Stripe webhook requests with Cashier's included [webhook signature verification](#verifying-webhook-signatures) middleware. +> **Warning** +> Make sure you protect incoming Stripe webhook requests with Cashier's included [webhook signature verification](#verifying-webhook-signatures) middleware. #### Webhooks & CSRF Protection @@ -1536,7 +1570,8 @@ The `charge` method will throw an exception if the charge fails. If the charge i // } -> {note} The `charge` method accepts the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. +> **Warning** +> The `charge` method accepts the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. ### Charge With Invoice @@ -1565,9 +1600,10 @@ Alternatively, you may use the `invoiceFor` method to make a "one-off" charge ag $user->invoiceFor('One Time Fee', 500); -Although the `invoiceFor` method is available for you to use, it is recommendeded that you use the `invoicePrice` and `tabPrice` methods with pre-defined prices. By doing so, you will have access to better analytics and data within your Stripe dashboard regarding your sales on a per-product basis. +Although the `invoiceFor` method is available for you to use, it is recommended that you use the `invoicePrice` and `tabPrice` methods with pre-defined prices. By doing so, you will have access to better analytics and data within your Stripe dashboard regarding your sales on a per-product basis. -> {note} The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. +> **Warning** +> The `invoice`, `invoicePrice`, and `invoiceFor` methods will create a Stripe invoice which will retry failed billing attempts. If you do not want invoices to retry failed charges, you will need to close them using the Stripe API after the first failed charge. ### Creating Payment Intents @@ -1598,7 +1634,8 @@ When using the `pay` method, the default payment methods that are enabled within return $payment->client_secret; }); -> {note} The `pay` and `payWith` methods accept the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. +> **Warning** +> The `pay` and `payWith` methods accept the payment amount in the lowest denominator of the currency used by your application. For example, if customers are paying in United States Dollars, amounts should be specified in pennies. ### Refunding Charges @@ -1649,7 +1686,7 @@ To retrieve the upcoming invoice for a customer, you may use the `upcomingInvoic $invoice = $user->upcomingInvoice(); -Similary, if the customer has multiple subscriptions, you can also retrieve the upcoming invoice for a specific subscription: +Similarly, if the customer has multiple subscriptions, you can also retrieve the upcoming invoice for a specific subscription: $invoice = $user->subscription('default')->upcomingInvoice(); @@ -1667,18 +1704,21 @@ You may pass an array of prices to the `previewInvoice` method in order to previ ### Generating Invoice PDFs +Before generating invoice PDFs, you should use Composer to install the Dompdf library, which is the default invoice renderer for Cashier: + +```php +composer require dompdf/dompdf +``` + From within a route or controller, you may use the `downloadInvoice` method to generate a PDF download of a given invoice. This method will automatically generate the proper HTTP response needed to download the invoice: use Illuminate\Http\Request; Route::get('/user/invoice/{invoice}', function (Request $request, $invoiceId) { - return $request->user()->downloadInvoice($invoiceId, [ - 'vendor' => 'Your Company', - 'product' => 'Your Product', - ]); + return $request->user()->downloadInvoice($invoiceId); }); -By default, all data on the invoice is derived from the customer and invoice data stored in Stripe. However, you can customize some of this data by providing an array as the second argument to the `downloadInvoice` method. This array allows you to customize information such as your company and product details: +By default, all data on the invoice is derived from the customer and invoice data stored in Stripe. The filename is based on your `app.name` config value. However, you can customize some of this data by providing an array as the second argument to the `downloadInvoice` method. This array allows you to customize information such as your company and product details: return $request->user()->downloadInvoice($invoiceId, [ 'vendor' => 'Your Company', @@ -1689,7 +1729,7 @@ By default, all data on the invoice is derived from the customer and invoice dat 'email' => 'info@example.com', 'url' => 'https://example.com', 'vendorVat' => 'BE123456789', - ], 'my-invoice'); + ]); The `downloadInvoice` method also allows for a custom filename via its third argument. This filename will automatically be suffixed with `.pdf`: @@ -1769,7 +1809,7 @@ When defining your `success_url` checkout option, you may instruct Stripe to add Route::get('/product-checkout', function (Request $request) { return $request->user()->checkout(['price_tshirt' => 1], [ - 'success_url' => route('checkout-success') . '?session_id={CHECKOUT_SESSION_ID}', + 'success_url' => route('checkout-success').'?session_id={CHECKOUT_SESSION_ID}', 'cancel_url' => route('checkout-cancel'), ]); }); @@ -1804,12 +1844,14 @@ You can also perform a simple charge for an ad-hoc product that has not been cre return $request->user()->checkoutCharge(1200, 'T-Shirt', 5); }); -> {note} When using the `checkoutCharge` method, Stripe will always create a new product and price in your Stripe dashboard. Therefore, we recommend that you create the products up front in your Stripe dashboard and use the `checkout` method instead. +> **Warning** +> When using the `checkoutCharge` method, Stripe will always create a new product and price in your Stripe dashboard. Therefore, we recommend that you create the products up front in your Stripe dashboard and use the `checkout` method instead. ### Subscription Checkouts -> {note} Using Stripe Checkout for subscriptions requires you to enable the `customer.subscription.created` webhook in your Stripe dashboard. This webhook will create the subscription record in your database and store all of the relevant subscription items. +> **Warning** +> Using Stripe Checkout for subscriptions requires you to enable the `customer.subscription.created` webhook in your Stripe dashboard. This webhook will create the subscription record in your database and store all of the relevant subscription items. You may also use Stripe Checkout to initiate subscriptions. After defining your subscription with Cashier's subscription builder methods, you may call the `checkout `method. When a customer visits this route they will be redirected to Stripe's Checkout page: @@ -1845,7 +1887,8 @@ Of course, you can also enable promotion codes for subscription checkouts: ->checkout(); }); -> {note} Unfortunately Stripe Checkout does not support all subscription billing options when starting subscriptions. Using the `anchorBillingCycleOn` method on the subscription builder, setting proration behavior, or setting payment behavior will not have any effect during Stripe Checkout sessions. Please consult [the Stripe Checkout Session API documentation](https://stripe.com/docs/api/checkout/sessions/create) to review which parameters are available. +> **Warning** +> Unfortunately Stripe Checkout does not support all subscription billing options when starting subscriptions. Using the `anchorBillingCycleOn` method on the subscription builder, setting proration behavior, or setting payment behavior will not have any effect during Stripe Checkout sessions. Please consult [the Stripe Checkout Session API documentation](https://stripe.com/docs/api/checkout/sessions/create) to review which parameters are available. #### Stripe Checkout & Trial Periods @@ -1872,7 +1915,8 @@ Checkout also supports collecting a customer's Tax ID. To enable this on a check When this method is invoked, a new checkbox will be available to the customer that allows them to indicate if they're purchasing as a company. If so, they will have the opportunity to provide their Tax ID number. -> {note} If you have already configured [automatic tax collection](#tax-configuration) in your application's service provider then this feature will be enabled automatically and there is no need to invoke the `collectTaxIds` method. +> **Warning** +> If you have already configured [automatic tax collection](#tax-configuration) in your application's service provider then this feature will be enabled automatically and there is no need to invoke the `collectTaxIds` method. ## Handling Failed Payments @@ -1945,7 +1989,8 @@ You can derive the specific status of an incomplete payment by inspecting the `p If your business or one of your customers is based in Europe you will need to abide by the EU's Strong Customer Authentication (SCA) regulations. These regulations were imposedĀ in September 2019 by the European Union to prevent payment fraud. Luckily, Stripe and Cashier are prepared for building SCA compliant applications. -> {note} Before getting started, review [Stripe's guide on PSD2 and SCA](https://stripe.com/guides/strong-customer-authentication) as well as their [documentation on the new SCA APIs](https://stripe.com/docs/strong-customer-authentication). +> **Warning** +> Before getting started, review [Stripe's guide on PSD2 and SCA](https://stripe.com/guides/strong-customer-authentication) as well as their [documentation on the new SCA APIs](https://stripe.com/docs/strong-customer-authentication). ### Payments Requiring Additional Confirmation @@ -1972,7 +2017,8 @@ CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment To ensure that off-session payment confirmation notifications are delivered, verify that [Stripe webhooks are configured](#handling-stripe-webhooks) for your application and the `invoice.payment_action_required` webhook is enabled in your Stripe dashboard. In addition, your `Billable` model should also use Laravel's `Illuminate\Notifications\Notifiable` trait. -> {note} Notifications will be sent even when customers are manually making a payment that requires additional confirmation. Unfortunately, there is no way for Stripe to know that the payment was done manually or "off-session". But, a customer will simply see a "Payment Successful" message if they visit the payment page after already confirming their payment. The customer will not be allowed to accidentally confirm the same payment twice and incur an accidental second charge. +> **Warning** +> Notifications will be sent even when customers are manually making a payment that requires additional confirmation. Unfortunately, there is no way for Stripe to know that the payment was done manually or "off-session". But, a customer will simply see a "Payment Successful" message if they visit the payment page after already confirming their payment. The customer will not be allowed to accidentally confirm the same payment twice and incur an accidental second charge. ## Stripe SDK @@ -2008,4 +2054,5 @@ To get started, add the **testing** version of your Stripe secret to your `phpun Now, whenever you interact with Cashier while testing, it will send actual API requests to your Stripe testing environment. For convenience, you should pre-fill your Stripe testing account with subscriptions / prices that you may use during testing. -> {tip} In order to test a variety of billing scenarios, such as credit card denials and failures, you may use the vast range of [testing card numbers and tokens](https://stripe.com/docs/testing) provided by Stripe. +> **Note** +> In order to test a variety of billing scenarios, such as credit card denials and failures, you may use the vast range of [testing card numbers and tokens](https://stripe.com/docs/testing) provided by Stripe. diff --git a/blade.md b/blade.md index 5a8b8a2d9ee..a54edff42d7 100644 --- a/blade.md +++ b/blade.md @@ -10,7 +10,7 @@ - [Loops](#loops) - [The Loop Variable](#the-loop-variable) - [Conditional Classes](#conditional-classes) - - [Checked / Selected / Disabled](#checked-and-selected) + - [Additional Attributes](#additional-attributes) - [Including Subviews](#including-subviews) - [The `@once` Directive](#the-once-directive) - [Raw PHP](#raw-php) @@ -54,7 +54,8 @@ Blade views may be returned from routes or controllers using the global `view` h return view('greeting', ['name' => 'Finn']); }); -> {tip} Want to take your Blade templates to the next level and build dynamic interfaces with ease? Check out [Laravel Livewire](https://laravel-livewire.com). +> **Note** +> Want to take your Blade templates to the next level and build dynamic interfaces with ease? Check out [Laravel Livewire](https://laravel-livewire.com). ## Displaying Data @@ -71,7 +72,8 @@ You may display the contents of the `name` variable like so: Hello, {{ $name }}. ``` -> {tip} Blade's `{{ }}` echo statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks. +> **Note** +> 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: @@ -113,7 +115,8 @@ By default, Blade `{{ }}` statements are automatically sent through PHP's `htmls 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. +> **Warning** +> 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 @@ -165,7 +168,8 @@ The latest versions of the Laravel application skeleton include a `Js` facade, w ``` -> {note} You should only use the `Js::from` method 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. +> **Warning** +> You should only use the `Js::from` method 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. #### The `@verbatim` Directive @@ -340,7 +344,8 @@ In addition to conditional statements, Blade provides simple directives for work @endwhile ``` -> {tip} While iterating through a `foreach` loop, 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. +> **Note** +> While iterating through a `foreach` loop, 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 skip the current iteration or end the loop using the `@continue` and `@break` directives: @@ -437,8 +442,8 @@ The `@class` directive conditionally compiles a CSS class string. The directive ``` - -### Checked / Selected / Disabled + +### Additional Attributes For convenience, you may use the `@checked` directive to easily indicate if a given HTML checkbox input is "checked". This directive will echo `checked` if the provided condition evaluates to `true`: @@ -467,10 +472,29 @@ Additionally, the `@disabled` directive may be used to indicate if a given eleme ``` +Moreover, the `@readonly` directive may be used to indicate if a given element should be "readonly": + +```blade +isNotAdmin()) /> +``` + +In addition, the `@required` directive may be used to indicate if a given element should be "required": + +```blade +isAdmin()) /> +``` + ### 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. +> **Note** +> 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: @@ -510,7 +534,8 @@ To include the first view that exists from a given array of views, you may use t @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. +> **Warning** +> 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 @@ -529,7 +554,8 @@ You may also pass a fourth argument to the `@each` directive. This argument dete @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. +> **Warning** +> 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 @@ -581,7 +607,7 @@ Blade also allows you to define comments in your views. However, unlike HTML com 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: +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: ```shell php artisan make:component Alert @@ -595,7 +621,7 @@ You may also create components within subdirectories: php artisan make:component Forms/Input ``` -The command above will create an `Input` component in the `App\View\Components\Forms` directory and the view will be placed in the `resources/views/components/forms` directory. +The command above will create an `Input` component in the `app/View/Components/Forms` directory and the view will be placed in the `resources/views/components/forms` directory. If you would like to create an anonymous component (a component with only a Blade template and no class), you may use the `--view` flag when invoking the `make:component` command: @@ -662,7 +688,7 @@ To display a component, you may use a Blade component tag within one of your Bla ``` -If the component class is nested deeper within the `App\View\Components` directory, you may use the `.` character to indicate directory nesting. For example, if we assume a component is located at `App\View\Components\Inputs\Button.php`, we may render it like so: +If the component class is nested deeper within the `app/View/Components` directory, you may use the `.` character to indicate directory nesting. For example, if we assume a component is located at `app/View/Components/Inputs/Button.php`, we may render it like so: ```blade @@ -793,7 +819,7 @@ In addition to public variables being available to your component template, any You may execute this method from your component template by invoking the variable matching the name of the method: ```blade - ``` @@ -892,7 +918,8 @@ All of the attributes that are not part of the component's constructor will auto ``` -> {note} Using directives such as `@env` within component tags is not supported at this time. For example, `` will not be compiled. +> **Warning** +> Using directives such as `@env` within component tags is not supported at this time. For example, `` will not be compiled. #### Default / Merged Attributes @@ -938,7 +965,8 @@ If you need to merge other attributes onto your component, you can chain the `me ``` -> {tip} If you need to conditionally compile classes on other HTML elements that shouldn't receive merged attributes, you can use the [`@class` directive](#conditional-classes). +> **Note** +> If you need to conditionally compile classes on other HTML elements that shouldn't receive merged attributes, you can use the [`@class` directive](#conditional-classes). #### Non-Class Attribute Merging @@ -1173,7 +1201,8 @@ Sometimes you may need to render a component but not know which component should ### 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. +> **Warning** +> 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. @@ -1326,7 +1355,8 @@ Because the `color` prop was only passed into the parent (``), it won't ``` -> {note} The `@aware` directive can not access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component can not be accessed by the `@aware` directive. +> **Warning** +> The `@aware` directive can not access parent data that is not explicitly passed to the parent component via HTML attributes. Default `@props` values that are not explicitly passed to the parent component can not be accessed by the `@aware` directive. ### Anonymous Component Namespaces @@ -1478,7 +1508,8 @@ When defining a child view, use the `@extends` Blade directive to specify which 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. +> **Note** +> 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: @@ -1573,6 +1604,14 @@ Blade allows you to push to named stacks which can be rendered somewhere else in @endpush ``` +If you would like to `@push` content if a given boolean expression evaluates to `true`, you may use the `@pushIf` directive: + +```blade +@pushIf($shouldPush, 'scripts') + +@endPushIf +``` + 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: ```blade @@ -1674,7 +1713,8 @@ As you can see, we will chain the `format` method onto whatever expression is pa format('m/d/Y H:i'); ?> -> {note} After updating the logic of a Blade directive, you will need to delete all of the cached Blade views. The cached Blade views may be removed using the `view:clear` Artisan command. +> **Warning** +> After updating the logic of a Blade directive, you will need to delete all of the cached Blade views. The cached Blade views may be removed using the `view:clear` Artisan command. ### Custom Echo Handlers diff --git a/broadcasting.md b/broadcasting.md index 57a61d1c616..15eb33e2323 100644 --- a/broadcasting.md +++ b/broadcasting.md @@ -54,7 +54,8 @@ The core concepts behind broadcasting are simple: clients connect to named chann 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) and [soketi](https://docs.soketi.app/) 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). +> **Note** +> Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events). ## Server Side Installation @@ -163,13 +164,14 @@ Once Echo is installed, you are ready to create a fresh Echo instance in your ap ```js import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; -window.Pusher = require('pusher-js'); +window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', - key: process.env.MIX_PUSHER_APP_KEY, - cluster: process.env.MIX_PUSHER_APP_CLUSTER, + key: import.meta.env.VITE_PUSHER_APP_KEY, + cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, forceTLS: true }); ``` @@ -180,7 +182,8 @@ Once you have uncommented and adjusted the Echo configuration according to your npm run dev ``` -> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Laravel Mix](/docs/{{version}}/mix). +> **Note** +> To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). #### Using An Existing Client Instance @@ -189,13 +192,16 @@ If you already have a pre-configured Pusher Channels client instance that you wo ```js import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; -const client = require('pusher-js'); +const options = { + broadcaster: 'pusher', + key: 'your-pusher-channels-key' +} window.Echo = new Echo({ - broadcaster: 'pusher', - key: 'your-pusher-channels-key', - client: client + ...options, + client: new Pusher(options.key, options) }); ``` @@ -216,12 +222,13 @@ Once Echo is installed, you are ready to create a fresh Echo instance in your ap ```js import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; -window.Pusher = require('pusher-js'); +window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', - key: process.env.MIX_ABLY_PUBLIC_KEY, + key: import.meta.env.VITE_ABLY_PUBLIC_KEY, wsHost: 'realtime-pusher.ably.io', wsPort: 443, disableStats: true, @@ -229,7 +236,7 @@ window.Echo = new Echo({ }); ``` -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. +Note that our Ably Echo configuration references a `VITE_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: @@ -237,7 +244,8 @@ Once you have uncommented and adjusted the Echo configuration according to your npm run dev ``` -> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Laravel Mix](/docs/{{version}}/mix). +> **Note** +> To learn more about compiling your application's JavaScript assets, please consult the documentation on [Vite](/docs/{{version}}/vite). ## Concept Overview @@ -246,7 +254,8 @@ Laravel's event broadcasting allows you to broadcast your server-side Laravel ev 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. -> {tip} If you would like to explore open source alternatives to Pusher, check out the [open source alternatives](#open-source-alternatives). +> **Note** +> If you would like to explore open source alternatives to Pusher, check out the [open source alternatives](#open-source-alternatives). ### Using An Example Application @@ -505,7 +514,8 @@ If your queue connection's `after_commit` configuration option is set to `false` public $afterCommit = true; } -> {tip} To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). +> **Note** +> To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). ## Authorizing Channels @@ -587,7 +597,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. +> **Warning** +> 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. #### Authorization Callback Authentication @@ -647,7 +658,8 @@ Finally, you may place the authorization logic for your channel in the channel c } } -> {tip} Like many other classes in Laravel, channel classes will automatically be resolved by the [service container](/docs/{{version}}/container). So, you may type-hint any dependencies required by your channel in its constructor. +> **Note** +> Like many other classes in Laravel, channel classes will automatically be resolved by the [service container](/docs/{{version}}/container). So, you may type-hint any dependencies required by your channel in its constructor. ## Broadcasting Events @@ -678,7 +690,8 @@ axios.post('/task', task) 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. +> **Warning** +> Your event must use the `Illuminate\Broadcasting\InteractsWithSockets` trait in order to call the `toOthers` method. #### Configuration @@ -748,9 +761,9 @@ If you would like to listen for events on a private channel, use the `private` m ```js Echo.private(`orders.${this.order.id}`) - .listen(...) - .listen(...) - .listen(...); + .listen(/* ... */) + .listen(/* ... */) + .listen(/* ... */); ``` @@ -865,9 +878,9 @@ As typical of other types of events, you may listen for events sent to presence ```js Echo.join(`chat.${roomId}`) - .here(...) - .joining(...) - .leaving(...) + .here(/* ... */) + .joining(/* ... */) + .leaving(/* ... */) .listen('NewMessage', (e) => { // }); @@ -876,7 +889,8 @@ Echo.join(`chat.${roomId}`) ## Model Broadcasting -> {note} Before reading the following documentation about model broadcasting, we recommend you become familiar with the general concepts of Laravel's model broadcasting services as well as how to manually create and listen to broadcast events. +> **Warning** +> Before reading the following documentation about model broadcasting, we recommend you become familiar with the general concepts of Laravel's model broadcasting services as well as how to manually create and listen to broadcast events. It is common to broadcast events when your application's [Eloquent models](/docs/{{version}}/eloquent) are created, updated, or deleted. Of course, this can easily be accomplished by manually [defining custom events for Eloquent model state changes](/docs/{{version}}/eloquent#events) and marking those events with the `ShouldBroadcast` interface. @@ -1070,7 +1084,8 @@ Echo.private(`App.Models.User.${this.user.id}`) ## Client Events -> {tip} When using [Pusher Channels](https://pusher.com/channels), you must enable the "Client Events" option in the "App Settings" section of your [application dashboard](https://dashboard.pusher.com/) in order to send client events. +> **Note** +> When using [Pusher Channels](https://pusher.com/channels), you must enable the "Client Events" option in the "App Settings" section of your [application dashboard](https://dashboard.pusher.com/) in order to send client events. Sometimes you may wish to broadcast an event to other connected clients without hitting your Laravel application at all. This can be particularly useful for things like "typing" notifications, where you want to alert users of your application that another user is typing a message on a given screen. diff --git a/cache.md b/cache.md index 6848361b9f6..0c06d04859a 100644 --- a/cache.md +++ b/cache.md @@ -50,7 +50,8 @@ When using the `database` cache driver, you will need to setup a table to contai $table->integer('expiration'); }); -> {tip} You may also use the `php artisan cache:table` Artisan command to generate a migration with the proper schema. +> **Note** +> You may also use the `php artisan cache:table` Artisan command to generate a migration with the proper schema. #### Memcached @@ -141,7 +142,7 @@ The `Cache` facade's `get` method is used to retrieve items from the cache. If t You may even pass a closure as the default value. The result of the closure will be returned if the specified item does not exist in the cache. Passing a closure allows you to defer the retrieval of default values from a database or other external service: $value = Cache::get('key', function () { - return DB::table(...)->get(); + return DB::table(/* ... */)->get(); }); @@ -216,7 +217,8 @@ The `forever` method may be used to store an item in the cache permanently. Sinc Cache::forever('key', 'value'); -> {tip} If you are using the Memcached driver, items that are stored "forever" may be removed when the cache reaches its size limit. +> **Note** +> If you are using the Memcached driver, items that are stored "forever" may be removed when the cache reaches its size limit. ### Removing Items From The Cache @@ -235,7 +237,8 @@ You may clear the entire cache using the `flush` method: Cache::flush(); -> {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. +> **Warning** +> 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. ### The Cache Helper @@ -256,17 +259,19 @@ 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 the facade](/docs/{{version}}/mocking#mocking-facades). +> **Note** +> 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). ## Cache Tags -> {note} Cache tags are not supported when using the `file`, `dynamodb`, or `database` cache drivers. Furthermore, when using multiple tags with caches that are stored "forever", performance will be best with a driver such as `memcached`, which automatically purges stale records. +> **Warning** +> Cache tags are not supported when using the `file`, `dynamodb`, or `database` cache drivers. Furthermore, when using multiple tags with caches that are stored "forever", performance will be best with a driver such as `memcached`, which automatically purges stale records. ### 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` a value into 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. Items stored via tags may not be accessed without also providing the tags that were used to store the value. For example, let's access a tagged cache and `put` a value into the cache: Cache::tags(['people', 'artists'])->put('John', $john, $seconds); @@ -295,7 +300,8 @@ In contrast, this statement would remove only cached values tagged with `authors ## Atomic Locks -> {note} To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. +> **Warning** +> To utilize this feature, your application must be using the `memcached`, `redis`, `dynamodb`, `database`, `file`, or `array` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server. ### Driver Prerequisites @@ -411,7 +417,8 @@ We just need to implement each of these methods using a MongoDB connection. For return Cache::repository(new MongoStore); }); -> {tip} If you're wondering where to put your custom cache driver code, you could create an `Extensions` namespace within your `app` directory. However, keep in mind that Laravel does not have a rigid application structure and you are free to organize your application according to your preferences. +> **Note** +> If you're wondering where to put your custom cache driver code, you could create an `Extensions` namespace within your `app` directory. However, keep in mind that Laravel does not have a rigid application structure and you are free to organize your application according to your preferences. ### Registering The Driver diff --git a/cashier-paddle.md b/cashier-paddle.md index 939276c8da5..af8d5bf757f 100644 --- a/cashier-paddle.md +++ b/cashier-paddle.md @@ -64,7 +64,8 @@ First, install the Cashier package for Paddle using the Composer package manager composer require laravel/cashier-paddle ``` -> {note} To ensure Cashier properly handles all Paddle events, remember to [set up Cashier's webhook handling](#handling-paddle-webhooks). +> **Warning** +> To ensure Cashier properly handles all Paddle events, remember to [set up Cashier's webhook handling](#handling-paddle-webhooks). ### Paddle Sandbox @@ -175,7 +176,8 @@ In addition to configuring Cashier's currency, you may also specify a locale to CASHIER_CURRENCY_LOCALE=nl_BE ``` -> {note} In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. +> **Warning** +> In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server. ### Overriding Default Models @@ -244,7 +246,8 @@ The Paddle checkout widget is asynchronous. Once the user creates or updates a s For more information on pay links, you may review [the Paddle API documentation on pay link generation](https://developer.paddle.com/api-reference/product-api/pay-links/createpaylink). -> {note} After a subscription state change, the delay for receiving the corresponding webhook is typically minimal but you should account for this in your application by considering that your user's subscription might not be immediately available after completing the checkout. +> **Warning** +> After a subscription state change, the delay for receiving the corresponding webhook is typically minimal but you should account for this in your application by considering that your user's subscription might not be immediately available after completing the checkout. #### Manually Rendering Pay Links @@ -301,7 +304,8 @@ $options = [ Please consult Paddle's [guide on Inline Checkout](https://developer.paddle.com/guides/how-tos/checkout/inline-checkout) as well as their [parameter reference](https://developer.paddle.com/reference/paddle-js/parameters) for further details on the inline checkout's available options. -> {note} If you would like to also use the `passthrough` option when specifying custom options, you should provide a key / value array as its value. Cashier will automatically handle converting the array to a JSON string. In addition, the `customer_id` passthrough option is reserved for internal Cashier usage. +> **Warning** +> If you would like to also use the `passthrough` option when specifying custom options, you should provide a key / value array as its value. Cashier will automatically handle converting the array to a JSON string. In addition, the `customer_id` passthrough option is reserved for internal Cashier usage. #### Manually Rendering An Inline Checkout @@ -431,7 +435,8 @@ You may display the original listed prices (without coupon discounts) using the ``` -> {note} When using the prices API, Paddle only allows applying coupons to one-time purchase products and not to subscription plans. +> **Warning** +> When using the prices API, Paddle only allows applying coupons to one-time purchase products and not to subscription plans. ## Customers @@ -540,7 +545,8 @@ You can also pass an array of metadata using the `withMetadata` method: ->withMetadata(['key' => 'value']) ->create(); -> {note} When providing metadata, please avoid using `subscription_name` as a metadata key. This key is reserved for internal use by Cashier. +> **Warning** +> When providing metadata, please avoid using `subscription_name` as a metadata key. This key is reserved for internal use by Cashier. ### Checking Subscription Status @@ -649,7 +655,8 @@ If you would like subscriptions to still be considered active when they are `pas Cashier::keepPastDueSubscriptionsActive(); } -> {note} When a subscription is in a `past_due` state it cannot be changed until payment information has been updated. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in a `past_due` state. +> **Warning** +> When a subscription is in a `past_due` state it cannot be changed until payment information has been updated. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in a `past_due` state. #### Subscription Scopes @@ -726,12 +733,13 @@ If you would like to swap plans and immediately invoice the user instead of wait $user->subscription('default')->swapAndInvoice($premium = 34567); -> {note} Plans may not be swapped when a trial is active. For additional information regarding this limitation, please see the [Paddle documentation](https://developer.paddle.com/api-reference/subscription-api/users/updateuser#usage-notes). +> **Warning** +> Plans may not be swapped when a trial is active. For additional information regarding this limitation, please see the [Paddle documentation](https://developer.paddle.com/api-reference/subscription-api/users/updateuser#usage-notes). #### Prorations -By default, Paddle prorates charges when swapping between plans. The `noProrate` method may be used to update the subscription's without prorating the charges: +By default, Paddle prorates charges when swapping between plans. The `noProrate` method may be used to update the subscriptions without prorating the charges: $user->subscription('default')->noProrate()->swap($premium = 34567); @@ -822,7 +830,8 @@ To resume a paused a subscription, you may call the `unpause` method on the user $user->subscription('default')->unpause(); -> {note} A subscription cannot be modified while it is paused. If you want to swap to a different plan or update quantities you must resume the subscription first. +> **Warning** +> A subscription cannot be modified while it is paused. If you want to swap to a different plan or update quantities you must resume the subscription first. ### Cancelling Subscriptions @@ -843,7 +852,8 @@ If you wish to cancel a subscription immediately, you may call the `cancelNow` m $user->subscription('default')->cancelNow(); -> {note} Paddle's subscriptions cannot be resumed after cancellation. If your customer wishes to resume their subscription, they will have to subscribe to a new subscription. +> **Warning** +> Paddle's subscriptions cannot be resumed after cancellation. If your customer wishes to resume their subscription, they will have to subscribe to a new subscription. ## Subscription Trials @@ -851,7 +861,8 @@ If you wish to cancel a subscription immediately, you may call the `cancelNow` m ### With Payment Method Up Front -> {note} While trialing and collecting payment method details up front, Paddle prevents any subscription changes such as swapping plans or updating quantities. If you want to allow a customer to swap plans during a trial the subscription must be cancelled and recreated. +> **Warning** +> While trialing and collecting payment method details up front, Paddle prevents any subscription changes such as swapping plans or updating quantities. If you want to allow a customer to swap plans during a trial the subscription must be cancelled and recreated. If you would like to offer trial periods to your customers while still collecting payment method information up front, you should use the `trialDays` method when creating your subscription pay links: @@ -868,7 +879,8 @@ If you would like to offer trial periods to your customers while still collectin This method will set the trial period ending date on the subscription record within your application's database, as well as instruct Paddle to not begin billing the customer until after this date. -> {note} If the customer's subscription is not cancelled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date. +> **Warning** +> If the customer's subscription is not cancelled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date. You may determine if the user is within their trial period using either the `onTrial` method of the user instance or the `onTrial` method of the subscription instance. The two examples below are equivalent: @@ -880,6 +892,16 @@ You may determine if the user is within their trial period using either the `onT // } +To determine if an existing trial has expired, you may use the `hasExpiredTrial` methods: + + if ($user->hasExpiredTrial('default')) { + // + } + + if ($user->subscription('default')->hasExpiredTrial()) { + // + } + #### Defining Trial Days In Paddle / Cashier @@ -930,14 +952,15 @@ You may use the `onGenericTrial` method if you wish to know specifically that th // User is within their "generic" trial period... } -> {note} There is no way to extend or modify a trial period on a Paddle subscription after it has been created. +> **Warning** +> There is no way to extend or modify a trial period on a Paddle subscription after it has been created. ## Handling Paddle Webhooks Paddle can notify your application of a variety of events via webhooks. By default, a route that points to Cashier's webhook controller is registered by the Cashier service provider. This controller will handle all incoming webhook requests. -By default, this controller will automatically handle cancelling subscriptions that have too many failed charges ([as defined by your Paddle subscription settings](https://vendors.paddle.com/subscription-settings)), subscription updates, and payment method changes; however, as we'll soon discover, you can extend this controller to handle any Paddle webhook event you like. +By default, this controller will automatically handle cancelling subscriptions that have too many failed charges ([as defined by your Paddle dunning settings](https://vendors.paddle.com/recover-settings#dunning-form-id)), subscription updates, and payment method changes; however, as we'll soon discover, you can extend this controller to handle any Paddle webhook event you like. To ensure your application can handle Paddle webhooks, be sure to [configure the webhook URL in the Paddle control panel](https://vendors.paddle.com/alerts-webhooks). By default, Cashier's webhook controller responds to the `/paddle/webhook` URL path. The full list of all webhooks you should enable in the Paddle control panel are: @@ -947,7 +970,8 @@ To ensure your application can handle Paddle webhooks, be sure to [configure the - Payment Succeeded - Subscription Payment Succeeded -> {note} Make sure you protect incoming requests with Cashier's included [webhook signature verification](/docs/{{version}}/cashier-paddle#verifying-webhook-signatures) middleware. +> **Warning** +> Make sure you protect incoming requests with Cashier's included [webhook signature verification](/docs/{{version}}/cashier-paddle#verifying-webhook-signatures) middleware. #### Webhooks & CSRF Protection @@ -1130,7 +1154,8 @@ You may optionally specify a specific amount to refund as well as a reason for t $receipt->order_id, 5.00, 'Unused product time' ); -> {tip} You can use the `$refundRequestId` as a reference for the refund when contacting Paddle support. +> **Note** +> You can use the `$refundRequestId` as a reference for the refund when contacting Paddle support. ## Receipts diff --git a/collections.md b/collections.md index 1ea326907bd..427c5582acb 100644 --- a/collections.md +++ b/collections.md @@ -31,7 +31,8 @@ As mentioned above, the `collect` helper returns a new `Illuminate\Support\Colle $collection = collect([1, 2, 3]); -> {tip} The results of [Eloquent](/docs/{{version}}/eloquent) queries are always returned as `Collection` instances. +> **Note** +> The results of [Eloquent](/docs/{{version}}/eloquent) queries are always returned as `Collection` instances. ### Extending Collections @@ -103,6 +104,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [combine](#method-combine) [concat](#method-concat) [contains](#method-contains) +[containsOneItem](#method-containsoneitem) [containsStrict](#method-containsstrict) [count](#method-count) [countBy](#method-countBy) @@ -131,6 +133,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [get](#method-get) [groupBy](#method-groupby) [has](#method-has) +[hasAny](#method-hasany) [implode](#method-implode) [intersect](#method-intersect) [intersectByKeys](#method-intersectbykeys) @@ -178,11 +181,11 @@ For the majority of the remaining collection documentation, we'll discuss each m [search](#method-search) [shift](#method-shift) [shuffle](#method-shuffle) -[sliding](#method-sliding) [skip](#method-skip) [skipUntil](#method-skipuntil) [skipWhile](#method-skipwhile) [slice](#method-slice) +[sliding](#method-sliding) [sole](#method-sole) [some](#method-some) [sort](#method-sort) @@ -212,6 +215,7 @@ For the majority of the remaining collection documentation, we'll discuss each m [unlessEmpty](#method-unlessempty) [unlessNotEmpty](#method-unlessnotempty) [unwrap](#method-unwrap) +[value](#method-value) [values](#method-values) [when](#method-when) [whenEmpty](#method-whenempty) @@ -365,7 +369,8 @@ The `collect` method is primarily useful for converting [lazy collections](#lazy // [1, 2, 3] -> {tip} The `collect` method is especially useful when you have an instance of `Enumerable` and need a non-lazy collection instance. Since `collect()` is part of the `Enumerable` contract, you can safely use it to get a `Collection` instance. +> **Note** +> The `collect` method is especially useful when you have an instance of `Enumerable` and need a non-lazy collection instance. Since `collect()` is part of the `Enumerable` contract, you can safely use it to get a `Collection` instance. #### `combine()` {.collection-method} @@ -435,12 +440,30 @@ The `contains` method uses "loose" comparisons when checking item values, meanin For the inverse of `contains`, see the [doesntContain](#method-doesntcontain) method. + +#### `containsOneItem()` {.collection-method} + +The `containsOneItem` method determines whether the collection contains a single item: + + collect([])->containsOneItem(); + + // false + + collect(['1'])->containsOneItem(); + + // true + + collect(['1', '2'])->containsOneItem(); + + // false + #### `containsStrict()` {.collection-method} This method has the same signature as the [`contains`](#method-contains) method; however, all values are compared using "strict" comparisons. -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-contains). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-contains). #### `count()` {.collection-method} @@ -550,7 +573,8 @@ The `diff` method compares the collection against another collection or a plain // [1, 3, 5] -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-diff). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-diff). #### `diffAssoc()` {.collection-method} @@ -754,7 +778,8 @@ The `except` method returns all items in the collection except for those with th For the inverse of `except`, see the [only](#method-only) method. -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-except). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-except). #### `filter()` {.collection-method} @@ -936,7 +961,8 @@ The `forget` method removes an item from the collection by its key: // ['framework' => 'laravel'] -> {note} Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. +> **Warning** +> Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. #### `forPage()` {.collection-method} @@ -1082,6 +1108,21 @@ The `has` method determines if a given key exists in the collection: // false + +#### `hasAny()` {.collection-method} + +The `hasAny` method determines whether any of the given keys exist in the collection: + + $collection = collect(['account_id' => 1, 'product' => 'Desk', 'amount' => 5]); + + $collection->hasAny(['product', 'price']); + + // true + + $collection->hasAny(['name', 'price']); + + // false + #### `implode()` {.collection-method} @@ -1102,6 +1143,14 @@ If the collection contains simple strings or numeric values, you should pass the // '1-2-3-4-5' +You may pass a closure to the `implode` method if you would like to format the values being imploded: + + $collection->implode(function ($item, $key) { + return strtoupper($item['product']); + }, ', '); + + // DESK, CHAIR + #### `intersect()` {.collection-method} @@ -1115,7 +1164,8 @@ The `intersect` method removes any values from the original collection that are // [0 => 'Desk', 2 => 'Chair'] -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-intersect). #### `intersectByKeys()` {.collection-method} @@ -1186,7 +1236,7 @@ The `keyBy` method keys the collection by the given key. If multiple items have You may also pass a callback to the method. The callback should return the value to key the collection by: - $keyed = $collection->keyBy(function ($item) { + $keyed = $collection->keyBy(function ($item, $key) { return strtoupper($item['product_id']); }); @@ -1282,7 +1332,8 @@ The `map` method iterates through the collection and passes each value to the gi // [2, 4, 6, 8, 10] -> {note} Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. +> **Warning** +> Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. #### `mapInto()` {.collection-method} @@ -1542,7 +1593,8 @@ The `only` method returns the items in the collection with the specified keys: For the inverse of `only`, see the [except](#method-except) method. -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-only). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-only). #### `pad()` {.collection-method} @@ -1675,9 +1727,15 @@ The `pluck` method also supports retrieving nested values using "dot" notation: $collection = collect([ [ + 'name' => 'Laracon', 'speakers' => [ 'first_day' => ['Rosa', 'Judith'], - 'second_day' => ['Angela', 'Kathleen'], + ], + ], + [ + 'name' => 'VueConf', + 'speakers' => [ + 'first_day' => ['Abigail', 'Joey'], ], ], ]); @@ -1686,7 +1744,7 @@ The `pluck` method also supports retrieving nested values using "dot" notation: $plucked->all(); - // ['Rosa', 'Judith'] + // [['Rosa', 'Judith'], ['Abigail', 'Joey']] If duplicate keys exist, the last matching element will be inserted into the plucked collection: @@ -1815,6 +1873,14 @@ You may pass an integer to `random` to specify how many items you would like to If the collection instance has fewer items than requested, the `random` method will throw an `InvalidArgumentException`. +The `random` method also accepts a closure, which will receive the current collection instance: + + $random = $collection->random(fn ($items) => min(10, count($items))); + + $random->all(); + + // [1, 2, 3, 4, 5] - (retrieved randomly) + #### `range()` {.collection-method} @@ -2024,35 +2090,6 @@ The `shuffle` method randomly shuffles the items in the collection: // [3, 2, 5, 1, 4] - (generated randomly) - -#### `sliding()` {.collection-method} - -The `sliding` method returns a new collection of chunks representing a "sliding window" view of the items in the collection: - - $collection = collect([1, 2, 3, 4, 5]); - - $chunks = $collection->sliding(2); - - $chunks->toArray(); - - // [[1, 2], [2, 3], [3, 4], [4, 5]] - -This is especially useful in conjunction with the [`eachSpread`](#method-eachspread) method: - - $transactions->sliding(2)->eachSpread(function ($previous, $current) { - $current->total = $previous->total + $current->amount; - }); - -You may optionally pass a second "step" value, which determines the distance between the first item of every chunk: - - $collection = collect([1, 2, 3, 4, 5]); - - $chunks = $collection->sliding(3, step: 2); - - $chunks->toArray(); - - // [[1, 2, 3], [3, 4, 5]] - #### `skip()` {.collection-method} @@ -2091,7 +2128,8 @@ You may also pass a simple value to the `skipUntil` method to skip all items unt // [3, 4] -> {note} If the given value is not found or the callback never returns `true`, the `skipUntil` method will return an empty collection. +> **Warning** +> If the given value is not found or the callback never returns `true`, the `skipUntil` method will return an empty collection. #### `skipWhile()` {.collection-method} @@ -2108,7 +2146,8 @@ The `skipWhile` method skips over items from the collection while the given call // [4] -> {note} If the callback never returns `false`, the `skipWhile` method will return an empty collection. +> **Warning** +> If the callback never returns `false`, the `skipWhile` method will return an empty collection. #### `slice()` {.collection-method} @@ -2133,6 +2172,35 @@ If you would like to limit the size of the returned slice, pass the desired size The returned slice will preserve keys by default. If you do not wish to preserve the original keys, you can use the [`values`](#method-values) method to reindex them. + +#### `sliding()` {.collection-method} + +The `sliding` method returns a new collection of chunks representing a "sliding window" view of the items in the collection: + + $collection = collect([1, 2, 3, 4, 5]); + + $chunks = $collection->sliding(2); + + $chunks->toArray(); + + // [[1, 2], [2, 3], [3, 4], [4, 5]] + +This is especially useful in conjunction with the [`eachSpread`](#method-eachspread) method: + + $transactions->sliding(2)->eachSpread(function ($previous, $current) { + $current->total = $previous->total + $current->amount; + }); + +You may optionally pass a second "step" value, which determines the distance between the first item of every chunk: + + $collection = collect([1, 2, 3, 4, 5]); + + $chunks = $collection->sliding(3, step: 2); + + $chunks->toArray(); + + // [[1, 2, 3], [3, 4, 5]] + #### `sole()` {.collection-method} @@ -2187,7 +2255,8 @@ The `sort` method sorts the collection. The sorted collection keeps the original 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. +> **Note** +> If you need to sort a collection of nested arrays or objects, see the [`sortBy`](#method-sortby) and [`sortByDesc`](#method-sortbydesc) methods. #### `sortBy()` {.collection-method} @@ -2530,7 +2599,8 @@ You may also pass a simple value to the `takeUntil` method to get the items unti // [1, 2] -> {note} If the given value is not found or the callback never returns `true`, the `takeUntil` method will return all items in the collection. +> **Warning** +> If the given value is not found or the callback never returns `true`, the `takeUntil` method will return all items in the collection. #### `takeWhile()` {.collection-method} @@ -2547,7 +2617,8 @@ The `takeWhile` method returns items in the collection until the given callback // [1, 2] -> {note} If the callback never returns `false`, the `takeWhile` method will return all items in the collection. +> **Warning** +> If the callback never returns `false`, the `takeWhile` method will return all items in the collection. #### `tap()` {.collection-method} @@ -2591,7 +2662,8 @@ 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 array underlying the collection, use the [`all`](#method-all) method instead. +> **Warning** +> `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. #### `toJson()` {.collection-method} @@ -2619,7 +2691,8 @@ The `transform` method iterates over the collection and calls the given callback // [2, 4, 6, 8, 10] -> {note} Unlike most other collection methods, `transform` modifies the collection itself. If you wish to create a new collection instead, use the [`map`](#method-map) method. +> **Warning** +> Unlike most other collection methods, `transform` modifies the collection itself. If you wish to create a new collection instead, use the [`map`](#method-map) method. #### `undot()` {.collection-method} @@ -2634,7 +2707,7 @@ The `undot` method expands a single-dimensional collection that uses "dot" notat 'address.suburb' => 'Detroit', 'address.state' => 'MI', 'address.postcode' => '48219' - ]) + ]); $person = $person->undot(); @@ -2722,7 +2795,8 @@ Finally, you may also pass your own closure to the `unique` method to specify wh The `unique` 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 [`uniqueStrict`](#method-uniquestrict) method to filter using "strict" comparisons. -> {tip} This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-unique). +> **Note** +> This method's behavior is modified when using [Eloquent Collections](/docs/{{version}}/eloquent-collections#method-unique). #### `uniqueStrict()` {.collection-method} @@ -2791,6 +2865,20 @@ The static `unwrap` method returns the collection's underlying items from the gi // 'John Doe' + +#### `value()` {.collection-method} + +The `value` method retrieves a given value from the first element of the collection: + + $collection = collect([ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Speaker', 'price' => 400], + ]); + + $value = $collection->value('price'); + + // 200 + #### `values()` {.collection-method} @@ -2956,7 +3044,7 @@ The `where` method filters the collection by a given key / value pair: The `where` 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 [`whereStrict`](#method-wherestrict) method to filter using "strict" comparisons. -Optionally, you may pass a comparison operator as the second parameter. +Optionally, you may pass a comparison operator as the second parameter. Supported operators are: '===', '!==', '!=', '==', '=', '<>', '>', '<', '>=', and '<=': $collection = collect([ ['name' => 'Jim', 'deleted_at' => '2019-01-01 00:00:00'], @@ -3216,7 +3304,8 @@ Likewise, we can use the `sum` higher order message to gather the total number o ### Introduction -> {note} Before learning more about Laravel's lazy collections, take some time to familiarize yourself with [PHP generators](https://www.php.net/manual/en/language.generators.overview.php). +> **Warning** +> Before learning more about Laravel's lazy collections, take some time to familiarize yourself with [PHP generators](https://www.php.net/manual/en/language.generators.overview.php). To supplement the already powerful `Collection` class, the `LazyCollection` class leverages PHP's [generators](https://www.php.net/manual/en/language.generators.overview.php) to allow you to work with very large datasets while keeping memory usage low. @@ -3277,7 +3366,20 @@ To create a lazy collection instance, you should pass a PHP generator function t Almost all methods available on the `Collection` class are also available on the `LazyCollection` class. Both of these classes implement the `Illuminate\Support\Enumerable` contract, which defines the following methods: -
+ + +
[all](#method-all) [average](#method-average) @@ -3392,7 +3494,8 @@ Almost all methods available on the `Collection` class are also available on the
-> {note} Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are **not** available on the `LazyCollection` class. +> **Warning** +> Methods that mutate the collection (such as `shift`, `pop`, `prepend` etc.) are **not** available on the `LazyCollection` class. ### Lazy Collection Methods diff --git a/configuration.md b/configuration.md index b44f1369a9a..f1af5d1a9ef 100644 --- a/configuration.md +++ b/configuration.md @@ -17,6 +17,21 @@ All of the configuration files for the Laravel framework are stored in the `conf 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. + +#### Application Overview + +In a hurry? You can get a quick overview of your application's configuration, drivers, and environment via the `about` Artisan command: + +```shell +php artisan about +``` + +If you're only interested in a particular section of the application overview output, you may filter for that section using the `--only` option: + +```shell +php artisan about --only=environment +``` + ## Environment Configuration @@ -28,7 +43,8 @@ Laravel's default `.env` file contains some common configuration values that may 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. +> **Note** +> 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 @@ -38,7 +54,7 @@ Your `.env` file should not be committed to your application's source control, s #### Additional Environment Files -Before loading your application's environment variables, Laravel determines if either the `APP_ENV` environment variable has been externally provided or if the `--env` CLI argument has been specified. If so, Laravel will attempt to load an `.env.[APP_ENV]` file if it exists. If it does not exist, the default `.env` file will be loaded. +Before loading your application's environment variables, Laravel determines if an `APP_ENV` environment variable has been externally provided or if the `--env` CLI argument has been specified. If so, Laravel will attempt to load an `.env.[APP_ENV]` file if it exists. If it does not exist, the default `.env` file will be loaded. ### Environment Variable Types @@ -65,7 +81,7 @@ APP_NAME="My Application" ### Retrieving Environment Configuration -All of the variables listed in this file will be loaded into the `$_ENV` PHP super-global when your application receives a request. However, you may use the `env` helper to retrieve values from these variables in your configuration files. In fact, if you review the Laravel configuration files, you will notice many of the options are already using this helper: +All of the variables listed in the `.env` file will be loaded into the `$_ENV` PHP super-global when your application receives a request. However, you may use the `env` function to retrieve values from these variables in your configuration files. In fact, if you review the Laravel configuration files, you will notice many of the options are already using this function: 'debug' => env('APP_DEBUG', false), @@ -90,19 +106,20 @@ You may also pass arguments to the `environment` method to determine if the envi // The environment is either local OR staging... } -> {tip} The current application environment detection can be overridden by defining a server-level `APP_ENV` environment variable. +> **Note** +> The current application environment detection can be overridden by defining a server-level `APP_ENV` environment variable. ## Accessing Configuration Values -You may easily access your configuration values using the global `config` helper function from anywhere in your application. The configuration values may be accessed using "dot" syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist: +You may easily access your configuration values using the global `config` function from anywhere in your application. The configuration values may be accessed using "dot" syntax, which includes the name of the file and option you wish to access. A default value may also be specified and will be returned if the configuration option does not exist: $value = config('app.timezone'); // Retrieve a default value if the configuration value does not exist... $value = config('app.timezone', 'Asia/Seoul'); -To set configuration values at runtime, pass an array to the `config` helper: +To set configuration values at runtime, pass an array to the `config` function: config(['app.timezone' => 'America/Chicago']); @@ -113,7 +130,8 @@ To give your application a speed boost, you should cache all of your configurati You should typically run the `php artisan config:cache` command as part of your production deployment 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; therefore, the `env` function will only return external, system level environment variables. +> **Warning** +> 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; therefore, the `env` function will only return external, system level environment variables. ## Debug Mode @@ -148,7 +166,7 @@ php artisan down --retry=60 #### Bypassing Maintenance Mode -Even while in maintenance mode, you may use the `secret` option to specify a maintenance mode bypass token: +To allow maintenance mode to be bypassed using a secret token, you may use the `secret` option to specify a maintenance mode bypass token: ```shell php artisan down --secret="1630542a-246b-4b66-afa1-dd72a4c43515" @@ -162,7 +180,8 @@ https://example.com/1630542a-246b-4b66-afa1-dd72a4c43515 When accessing this hidden route, you will then be redirected to the `/` route of the application. Once the cookie has been issued to your browser, you will be able to browse the application normally as if it was not in maintenance mode. -> {tip} Your maintenance mode secret should typically consist of alpha-numeric characters and, optionally, dashes. You should avoid using characters that have special meaning in URLs such as `?`. +> **Note** +> Your maintenance mode secret should typically consist of alpha-numeric characters and, optionally, dashes. You should avoid using characters that have special meaning in URLs such as `?`. #### Pre-Rendering The Maintenance Mode View @@ -193,7 +212,8 @@ To disable maintenance mode, use the `up` command: php artisan up ``` -> {tip} You may customize the default maintenance mode template by defining your own template at `resources/views/errors/503.blade.php`. +> **Note** +> You may customize the default maintenance mode template by defining your own template at `resources/views/errors/503.blade.php`. #### Maintenance Mode & Queues diff --git a/container.md b/container.md index 1a20681863a..ee41ac5bbff 100644 --- a/container.md +++ b/container.md @@ -137,7 +137,8 @@ As mentioned, you will typically be interacting with the container within servic // ... }); -> {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. +> **Note** +> 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 diff --git a/contributions.md b/contributions.md index 223a08c0317..fbe08faf9ab 100644 --- a/contributions.md +++ b/contributions.md @@ -38,6 +38,7 @@ The Laravel source code is managed on GitHub, and there are repositories for eac - [Laravel Horizon](https://github.com/laravel/horizon) - [Laravel Jetstream](https://github.com/laravel/jetstream) - [Laravel Passport](https://github.com/laravel/passport) +- [Laravel Pint](https://github.com/laravel/pint) - [Laravel Sail](https://github.com/laravel/sail) - [Laravel Sanctum](https://github.com/laravel/sanctum) - [Laravel Scout](https://github.com/laravel/scout) @@ -74,7 +75,7 @@ Informal discussion regarding bugs, new features, and implementation of existing ## Which Branch? -**All** bug fixes should be sent to the latest version that supports bug fixes (currently `8.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. +**All** bug fixes should be sent to the latest version that supports bug fixes (currently `9.x`). Bug fixes should **never** be sent to the `master` branch unless they fix features that exist only in the upcoming release. **Minor** features that are **fully backward compatible** with the current release may be sent to the latest stable branch (currently `9.x`). diff --git a/controllers.md b/controllers.md index 9cadef58600..50db5b3e8d1 100644 --- a/controllers.md +++ b/controllers.md @@ -59,7 +59,8 @@ You can define a route to this controller method like so: 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. +> **Note** +> 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 @@ -98,7 +99,8 @@ You may generate an invokable controller by using the `--invokable` option of th php artisan make:controller ProvisionServer --invokable ``` -> {tip} Controller stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). +> **Note** +> Controller stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). ## Controller Middleware @@ -359,7 +361,8 @@ If you need to add additional routes to a resource controller beyond the default 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. +> **Note** +> 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. ## Dependency Injection & Controllers diff --git a/csrf.md b/csrf.md index fab2546f7b7..c6a1f76f98c 100644 --- a/csrf.md +++ b/csrf.md @@ -94,7 +94,8 @@ Typically, you should place these kinds of routes outside of the `web` middlewar ]; } -> {tip} For convenience, the CSRF middleware is automatically disabled for all routes when [running tests](/docs/{{version}}/testing). +> **Note** +> For convenience, the CSRF middleware is automatically disabled for all routes when [running tests](/docs/{{version}}/testing). ## X-CSRF-TOKEN @@ -122,4 +123,5 @@ Laravel stores the current CSRF token in an encrypted `XSRF-TOKEN` cookie that i 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 the `X-XSRF-TOKEN` header for you. +> **Note** +> 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/database-testing.md b/database-testing.md index b149c7b1888..757d5c8ead6 100644 --- a/database-testing.md +++ b/database-testing.md @@ -2,21 +2,7 @@ - [Introduction](#introduction) - [Resetting The Database After Each Test](#resetting-the-database-after-each-test) -- [Defining Model Factories](#defining-model-factories) - - [Concept Overview](#concept-overview) - - [Generating Factories](#generating-factories) - - [Factory States](#factory-states) - - [Factory Callbacks](#factory-callbacks) -- [Creating Models Using Factories](#creating-models-using-factories) - - [Instantiating Models](#instantiating-models) - - [Persisting Models](#persisting-models) - - [Sequences](#sequences) -- [Factory Relationships](#factory-relationships) - - [Has Many Relationships](#has-many-relationships) - - [Belongs To Relationships](#belongs-to-relationships) - - [Many To Many Relationships](#many-to-many-relationships) - - [Polymorphic Relationships](#polymorphic-relationships) - - [Defining Relationships Within Factories](#defining-relationships-within-factories) +- [Model Factories](#model-factories) - [Running Seeders](#running-seeders) - [Available Assertions](#available-assertions) @@ -59,476 +45,20 @@ The `Illuminate\Foundation\Testing\RefreshDatabase` trait does not migrate your If you would like to totally reset the database using migrations, you may use the `Illuminate\Foundation\Testing\DatabaseMigrations` trait instead. However, the `DatabaseMigrations` trait is significantly slower than the `RefreshDatabase` trait. - -## Defining Model Factories + +## Model Factories - -### Concept Overview +When testing, you may need to insert a few records into your database before executing your test. Instead of manually specifying the value of each column when you create this test data, Laravel allows you to define a set of default attributes for each of your [Eloquent models](/docs/{{version}}/eloquent) using [model factories](/docs/{{version}}/eloquent-factories). -First, let's talk about Eloquent model factories. When testing, you may need to insert a few records into your database before executing your test. Instead of manually specifying the value of each column when you create this test data, Laravel allows you to define a set of default attributes for each of your [Eloquent models](/docs/{{version}}/eloquent) using model factories. - -To see an example of how to write a factory, take a look at the `database/factories/UserFactory.php` file in your application. This factory is included with all new Laravel applications and contains the following factory definition: - - namespace Database\Factories; - - use Illuminate\Database\Eloquent\Factories\Factory; - use Illuminate\Support\Str; - - class UserFactory extends Factory - { - /** - * Define the model's default state. - * - * @return array - */ - public function definition() - { - return [ - 'name' => $this->faker->name(), - 'email' => $this->faker->unique()->safeEmail(), - 'email_verified_at' => now(), - 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password - 'remember_token' => Str::random(10), - ]; - } - } - -As you can see, in their most basic form, factories are classes that extend Laravel's base factory class and define a `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory. - -Via the `faker` property, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing. - -> {tip} You can set your application's Faker locale by adding a `faker_locale` option to your `config/app.php` configuration file. - - -### Generating Factories - -To create a factory, execute the `make:factory` [Artisan command](/docs/{{version}}/artisan): - -```shell -php artisan make:factory PostFactory -``` - -The new factory class will be placed in your `database/factories` directory. - - -#### Model & Factory Discovery Conventions - -Once you have defined your factories, you may use the static `factory` method provided to your models by the `Illuminate\Database\Eloquent\Factories\HasFactory` trait in order to instantiate a factory instance for that model. - -The `HasFactory` trait's `factory` method will use conventions to determine the proper factory for the model the trait is assigned to. Specifically, the method will look for a factory in the `Database\Factories` namespace that has a class name matching the model name and is suffixed with `Factory`. If these conventions do not apply to your particular application or factory, you may overwrite the `newFactory` method on your model to return an instance of the model's corresponding factory directly: - - use Database\Factories\Administration\FlightFactory; - - /** - * Create a new factory instance for the model. - * - * @return \Illuminate\Database\Eloquent\Factories\Factory - */ - protected static function newFactory() - { - return FlightFactory::new(); - } - -Next, define a `model` property on the corresponding factory: - - use App\Administration\Flight; - use Illuminate\Database\Eloquent\Factories\Factory; - - class FlightFactory extends Factory - { - /** - * The name of the factory's corresponding model. - * - * @var string - */ - protected $model = Flight::class; - } - - -### Factory States - -State manipulation methods allow you to define discrete modifications that can be applied to your model factories in any combination. For example, your `Database\Factories\UserFactory` factory might contain a `suspended` state method that modifies one of its default attribute values. - -State transformation methods typically call the `state` method provided by Laravel's base factory class. The `state` method accepts a closure which will receive the array of raw attributes defined for the factory and should return an array of attributes to modify: - - /** - * Indicate that the user is suspended. - * - * @return \Illuminate\Database\Eloquent\Factories\Factory - */ - public function suspended() - { - return $this->state(function (array $attributes) { - return [ - 'account_status' => 'suspended', - ]; - }); - } - - -### Factory Callbacks - -Factory callbacks are registered using the `afterMaking` and `afterCreating` methods and allow you to perform additional tasks after making or creating a model. You should register these callbacks by defining a `configure` method on your factory class. This method will be automatically called by Laravel when the factory is instantiated: - - namespace Database\Factories; - - use App\Models\User; - use Illuminate\Database\Eloquent\Factories\Factory; - use Illuminate\Support\Str; - - class UserFactory extends Factory - { - /** - * Configure the model factory. - * - * @return $this - */ - public function configure() - { - return $this->afterMaking(function (User $user) { - // - })->afterCreating(function (User $user) { - // - }); - } - - // ... - } - - -## Creating Models Using Factories - - -### Instantiating Models - -Once you have defined your factories, you may use the static `factory` method provided to your models by the `Illuminate\Database\Eloquent\Factories\HasFactory` trait in order to instantiate a factory instance for that model. Let's take a look at a few examples of creating models. First, we'll use the `make` method to create models without persisting them to the database: +To learn more about creating and utilizing model factories to create models, please consult the complete [model factory documentation](/docs/{{version}}/eloquent-factories). Once you have defined a model factory, you may utilize the factory within your test to create models: use App\Models\User; public function test_models_can_be_instantiated() { - $user = User::factory()->make(); - - // Use model in tests... - } - -You may create a collection of many models using the `count` method: - - $users = User::factory()->count(3)->make(); - - -#### Applying States - -You may also apply any of your [states](#factory-states) to the models. If you would like to apply multiple state transformations to the models, you may simply call the state transformation methods directly: - - $users = User::factory()->count(5)->suspended()->make(); - - -#### Overriding Attributes - -If you would like to override some of the default values of your models, you may pass an array of values to the `make` method. Only the specified attributes will be replaced while the rest of the attributes remain set to their default values as specified by the factory: - - $user = User::factory()->make([ - 'name' => 'Abigail Otwell', - ]); - -Alternatively, the `state` method may be called directly on the factory instance to perform an inline state transformation: - - $user = User::factory()->state([ - 'name' => 'Abigail Otwell', - ])->make(); - -> {tip} [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled when creating models using factories. - - -### Persisting Models - -The `create` method instantiates model instances and persists them to the database using Eloquent's `save` method: - - use App\Models\User; - - public function test_models_can_be_persisted() - { - // Create a single App\Models\User instance... $user = User::factory()->create(); - // Create three App\Models\User instances... - $users = User::factory()->count(3)->create(); - - // Use model in tests... - } - -You may override the factory's default model attributes by passing an array of attributes to the `create` method: - - $user = User::factory()->create([ - 'name' => 'Abigail', - ]); - - -### Sequences - -Sometimes you may wish to alternate the value of a given model attribute for each created model. You may accomplish this by defining a state transformation as a sequence. For example, you may wish to alternate the value of an `admin` column between `Y` and `N` for each created user: - - use App\Models\User; - use Illuminate\Database\Eloquent\Factories\Sequence; - - $users = User::factory() - ->count(10) - ->state(new Sequence( - ['admin' => 'Y'], - ['admin' => 'N'], - )) - ->create(); - -In this example, five users will be created with an `admin` value of `Y` and five users will be created with an `admin` value of `N`. - -If necessary, you may include a closure as a sequence value. The closure will be invoked each time the sequence needs a new value: - - $users = User::factory() - ->count(10) - ->state(new Sequence( - fn ($sequence) => ['role' => UserRoles::all()->random()], - )) - ->create(); - -Within a sequence closure, you may access the `$index` or `$count` properties on the sequence instance that is injected into the closure. The `$index` property contains the number of iterations through the sequence that have occurred thus far, while the `$count` property contains the total number of times the sequence will be invoked: - - $users = User::factory() - ->count(10) - ->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index]) - ->create(); - - -## Factory Relationships - - -### Has Many Relationships - -Next, let's explore building Eloquent model relationships using Laravel's fluent factory methods. First, let's assume our application has an `App\Models\User` model and an `App\Models\Post` model. Also, let's assume that the `User` model defines a `hasMany` relationship with `Post`. We can create a user that has three posts using the `has` method provided by the Laravel's factories. The `has` method accepts a factory instance: - - use App\Models\Post; - use App\Models\User; - - $user = User::factory() - ->has(Post::factory()->count(3)) - ->create(); - -By convention, when passing a `Post` model to the `has` method, Laravel will assume that the `User` model must have a `posts` method that defines the relationship. If necessary, you may explicitly specify the name of the relationship that you would like to manipulate: - - $user = User::factory() - ->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: - - $user = User::factory() - ->has( - Post::factory() - ->count(3) - ->state(function (array $attributes, User $user) { - return ['user_type' => $user->type]; - }) - ) - ->create(); - - -#### Using Magic Methods - -For convenience, you may use Laravel's magic factory relationship methods to build relationships. For example, the following example will use convention to determine that the related models should be created via a `posts` relationship method on the `User` model: - - $user = User::factory() - ->hasPosts(3) - ->create(); - -When using magic methods to create factory relationships, you may pass an array of attributes to override on the related models: - - $user = User::factory() - ->hasPosts(3, [ - 'published' => false, - ]) - ->create(); - -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) { - return ['user_type' => $user->type]; - }) - ->create(); - - -### Belongs To Relationships - -Now that we have explored how to build "has many" relationships using factories, let's explore the inverse of the relationship. The `for` method may be used to define the parent model that factory created models belong to. For example, we can create three `App\Models\Post` model instances that belong to a single user: - - use App\Models\Post; - use App\Models\User; - - $posts = Post::factory() - ->count(3) - ->for(User::factory()->state([ - 'name' => 'Jessica Archer', - ])) - ->create(); - -If you already have a parent model instance that should be associated with the models you are creating, you may pass the model instance to the `for` method: - - $user = User::factory()->create(); - - $posts = Post::factory() - ->count(3) - ->for($user) - ->create(); - - -#### Using Magic Methods - -For convenience, you may use Laravel's magic factory relationship methods to define "belongs to" relationships. For example, the following example will use convention to determine that the three posts should belong to the `user` relationship on the `Post` model: - - $posts = Post::factory() - ->count(3) - ->forUser([ - 'name' => 'Jessica Archer', - ]) - ->create(); - - -### Many To Many Relationships - -Like [has many relationships](#has-many-relationships), "many to many" relationships may be created using the `has` method: - - use App\Models\Role; - use App\Models\User; - - $user = User::factory() - ->has(Role::factory()->count(3)) - ->create(); - - -#### Pivot Table Attributes - -If you need to define attributes that should be set on the pivot / intermediate table linking the models, you may use the `hasAttached` method. This method accepts an array of pivot table attribute names and values as its second argument: - - use App\Models\Role; - use App\Models\User; - - $user = User::factory() - ->hasAttached( - Role::factory()->count(3), - ['active' => true] - ) - ->create(); - -You may provide a closure based state transformation if your state change requires access to the related model: - - $user = User::factory() - ->hasAttached( - Role::factory() - ->count(3) - ->state(function (array $attributes, User $user) { - return ['name' => $user->name.' Role']; - }), - ['active' => true] - ) - ->create(); - -If you already have model instances that you would like to be attached to the models you are creating, you may pass the model instances to the `hasAttached` method. In this example, the same three roles will be attached to all three users: - - $roles = Role::factory()->count(3)->create(); - - $user = User::factory() - ->count(3) - ->hasAttached($roles, ['active' => true]) - ->create(); - - -#### Using Magic Methods - -For convenience, you may use Laravel's magic factory relationship methods to define many to many relationships. For example, the following example will use convention to determine that the related models should be created via a `roles` relationship method on the `User` model: - - $user = User::factory() - ->hasRoles(1, [ - 'name' => 'Editor' - ]) - ->create(); - - -### Polymorphic Relationships - -[Polymorphic relationships](/docs/{{version}}/eloquent-relationships#polymorphic-relationships) may also be created using factories. Polymorphic "morph many" relationships are created in the same way as typical "has many" relationships. For example, if a `App\Models\Post` model has a `morphMany` relationship with a `App\Models\Comment` model: - - use App\Models\Post; - - $post = Post::factory()->hasComments(3)->create(); - - -#### Morph To Relationships - -Magic methods may not be used to create `morphTo` relationships. Instead, the `for` method must be used directly and the name of the relationship must be explicitly provided. For example, imagine that the `Comment` model has a `commentable` method that defines a `morphTo` relationship. In this situation, we may create three comments that belong to a single post by using the `for` method directly: - - $comments = Comment::factory()->count(3)->for( - Post::factory(), 'commentable' - )->create(); - - -#### Polymorphic Many To Many Relationships - -Polymorphic "many to many" (`morphToMany` / `morphedByMany`) relationships may be created just like non-polymorphic "many to many" relationships: - - use App\Models\Tag; - use App\Models\Video; - - $videos = Video::factory() - ->hasAttached( - Tag::factory()->count(3), - ['public' => true] - ) - ->create(); - -Of course, the magic `has` method may also be used to create polymorphic "many to many" relationships: - - $videos = Video::factory() - ->hasTags(3, ['public' => true]) - ->create(); - - -### Defining Relationships Within Factories - -To define a relationship within your model factory, you will typically assign a new factory instance to the foreign key of the relationship. This is normally done for the "inverse" relationships such as `belongsTo` and `morphTo` relationships. For example, if you would like to create a new user when creating a post, you may do the following: - - use App\Models\User; - - /** - * Define the model's default state. - * - * @return array - */ - public function definition() - { - return [ - 'user_id' => User::factory(), - 'title' => $this->faker->title(), - 'content' => $this->faker->paragraph(), - ]; - } - -If the relationship's columns depend on the factory that defines it you may assign a closure to an attribute. The closure will receive the factory's evaluated attribute array: - - /** - * Define the model's default state. - * - * @return array - */ - public function definition() - { - return [ - 'user_id' => User::factory(), - 'user_type' => function (array $attributes) { - return User::find($attributes['user_id'])->type; - }, - 'title' => $this->faker->title(), - 'content' => $this->faker->paragraph(), - ]; + // ... } @@ -641,6 +171,13 @@ Assert that a table in the database does not contain records matching the given The `assertSoftDeleted` method may be used to assert a given Eloquent model has been "soft deleted": $this->assertSoftDeleted($user); + + +#### assertNotSoftDeleted + +The `assertNotSoftDeleted` method may be used to assert a given Eloquent model hasn't been "soft deleted": + + $this->assertNotSoftDeleted($user); #### assertModelExists diff --git a/database.md b/database.md index f55dfbce2d7..85f1a76eed2 100644 --- a/database.md +++ b/database.md @@ -6,8 +6,11 @@ - [Running SQL Queries](#running-queries) - [Using Multiple Database Connections](#using-multiple-database-connections) - [Listening For Query Events](#listening-for-query-events) + - [Monitoring Cumulative Query Time](#monitoring-cumulative-query-time) - [Database Transactions](#database-transactions) - [Connecting To The Database CLI](#connecting-to-the-database-cli) +- [Inspecting Your Databases](#inspecting-your-databases) +- [Monitoring Your Databases](#monitoring-your-databases) ## Introduction @@ -16,7 +19,7 @@ Almost every modern web application interacts with a database. Laravel makes int
-- MariaDB 10.2+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) +- MariaDB 10.3+ ([Version Policy](https://mariadb.org/about/#maintenance-policy)) - MySQL 5.7+ ([Version Policy](https://en.wikipedia.org/wiki/MySQL#Release_history)) - PostgreSQL 10.0+ ([Version Policy](https://www.postgresql.org/support/versioning/)) - SQLite 3.8.8+ @@ -213,7 +216,8 @@ Sometimes you may want to execute an SQL statement without binding any values. Y DB::unprepared('update users set votes = 100 where name = "Dries"'); -> {note} Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement. +> **Warning** +> Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement. #### Implicit Commits @@ -231,7 +235,7 @@ If your application defines multiple connections in your `config/database.php` c use Illuminate\Support\Facades\DB; - $users = DB::connection('sqlite')->select(...); + $users = DB::connection('sqlite')->select(/* ... */); You may access the raw, underlying PDO instance of a connection using the `getPdo` method on a connection instance: @@ -276,6 +280,44 @@ If you would like to specify a closure that is invoked for each SQL query execut } } + +### Monitoring Cumulative Query Time + +A common performance bottleneck of modern web applications is the amount of time they spend querying databases. Thankfully, Laravel can invoke a closure or callback of your choice when it spends too much time querying the database during a single request. To get started, provide a query time threshold (in milliseconds) and closure to the `whenQueryingForLongerThan` method. You may invoke this method in the `boot` method of a [service provider](/docs/{{version}}/providers): + + ## Database Transactions @@ -319,7 +361,8 @@ Lastly, you can commit a transaction via the `commit` method: DB::commit(); -> {tip} The `DB` facade's transaction methods control the transactions for both the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent). +> **Note** +> The `DB` facade's transaction methods control the transactions for both the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent). ## Connecting To The Database CLI @@ -336,3 +379,68 @@ If needed, you may specify a database connection name to connect to a database c php artisan db mysql ``` + +## Inspecting Your Databases + +Using the `db:show` and `db:table` Artisan commands, you can get valuable insight into your database and its associated tables. To see an overview of your database, including its size, type, number of open connections, and a summary of its tables, you may use the `db:show` command: + +```shell +php artisan db:show +``` + +You may specify which database connection should be inspected by providing the database connection name to the command via the `--database` option: + +```shell +php artisan db:show --database=pgsql +``` + +If you would like to include table row counts and database view details within the output of the command, you may provide the `--counts` and `--views` options, respectively. On large databases, retrieving row counts and view details can be slow: + +```shell +php artisan db:show --counts --views +``` + + +#### Table Overview + +If you would like to get an overview of an individual table within your database, you may execute the `db:table` Artisan command. This command provides a general overview of a database table, including its columns, types, attributes, keys, and indexes: + +```shell +php artisan db:table users +``` + + +## Monitoring Your Databases + +Using the `db:monitor` Artisan command, you can instruct Laravel to dispatch an `Illuminate\Database\Events\DatabaseBusy` event if your database is managing more than a specified number of open connections. + +To get started, you should schedule the `db:monitor` command to [run every minute](/docs/{{version}}/scheduling). The command accepts the names of the database connection configurations that you wish to monitor as well as the maximum number of open connections that should be tolerated before dispatching an event: + +```shell +php artisan db:monitor --databases=mysql,pgsql --max=100 +``` + +Scheduling this command alone is not enough to trigger a notification alerting you of the number of open connections. When the command encounters a database that has an open connection count that exceeds your threshold, a `DatabaseBusy` event will be dispatched. You should listen for this event within your application's `EventServiceProvider` in order to send a notification to you or your development team: + +```php +use App\Notifications\DatabaseApproachingMaxConnections; +use Illuminate\Database\Events\DatabaseBusy; +use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\Notification; + +/** + * Register any other events for your application. + * + * @return void + */ +public function boot() +{ + Event::listen(function (DatabaseBusy $event) { + Notification::route('mail', 'dev@example.com') + ->notify(new DatabaseApproachingMaxConnections( + $event->connectionName, + $event->connections + )); + }); +} +``` diff --git a/deployment.md b/deployment.md index 2e3fdd46f38..fd4ff5a9c76 100644 --- a/deployment.md +++ b/deployment.md @@ -97,7 +97,8 @@ When deploying to production, make sure that you are optimizing Composer's class composer install --optimize-autoloader --no-dev ``` -> {tip} In addition to optimizing the autoloader, you should always be sure to include a `composer.lock` file in your project's source control repository. Your project's dependencies can be installed much faster when a `composer.lock` file is present. +> **Note** +> In addition to optimizing the autoloader, you should always be sure to include a `composer.lock` file in your project's source control repository. Your project's dependencies can be installed much faster when a `composer.lock` file is present. ### Optimizing Configuration Loading @@ -110,7 +111,8 @@ php artisan config:cache This command will combine all of Laravel's configuration files into a single, cached file, which greatly reduces the number of trips the framework must make to the filesystem when loading your configuration values. -> {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 for `.env` variables will return `null`. +> **Warning** +> 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 for `.env` variables will return `null`. ### Optimizing Route Loading diff --git a/documentation.md b/documentation.md index 5da7f822cba..54cdd558ee3 100644 --- a/documentation.md +++ b/documentation.md @@ -6,6 +6,7 @@ - [Installation](/docs/{{version}}/installation) - [Configuration](/docs/{{version}}/configuration) - [Directory Structure](/docs/{{version}}/structure) + - [Frontend](/docs/{{version}}/frontend) - [Starter Kits](/docs/{{version}}/starter-kits) - [Deployment](/docs/{{version}}/deployment) - ## Architecture Concepts @@ -22,6 +23,7 @@ - [Responses](/docs/{{version}}/responses) - [Views](/docs/{{version}}/views) - [Blade Templates](/docs/{{version}}/blade) + - [Asset Bundling](/docs/{{version}}/vite) - [URL Generation](/docs/{{version}}/urls) - [Session](/docs/{{version}}/session) - [Validation](/docs/{{version}}/validation) @@ -32,7 +34,6 @@ - [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) @@ -66,6 +67,7 @@ - [Mutators / Casts](/docs/{{version}}/eloquent-mutators) - [API Resources](/docs/{{version}}/eloquent-resources) - [Serialization](/docs/{{version}}/eloquent-serialization) + - [Factories](/docs/{{version}}/eloquent-factories) - ## Testing - [Getting Started](/docs/{{version}}/testing) - [HTTP Tests](/docs/{{version}}/http-tests) @@ -83,8 +85,10 @@ - [Homestead](/docs/{{version}}/homestead) - [Horizon](/docs/{{version}}/horizon) - [Jetstream](https://jetstream.laravel.com) + - [Mix](/docs/{{version}}/mix) - [Octane](/docs/{{version}}/octane) - [Passport](/docs/{{version}}/passport) + - [Pint](/docs/{{version}}/pint) - [Sail](/docs/{{version}}/sail) - [Sanctum](/docs/{{version}}/sanctum) - [Scout](/docs/{{version}}/scout) diff --git a/dusk.md b/dusk.md index ec93a824062..3cdd87debb3 100644 --- a/dusk.md +++ b/dusk.md @@ -62,9 +62,10 @@ To get started, you should install [Google Chrome](https://www.google.com/chrome composer require --dev laravel/dusk ``` -> {note} If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. +> **Warning** +> If you are manually registering Dusk's service provider, you should **never** register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application. -After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory and an example Dusk test: +After installing the Dusk package, execute the `dusk:install` Artisan command. The `dusk:install` command will create a `tests/Browser` directory, an example Dusk test, and install the Chrome Driver binary for your operation system: ```shell php artisan dusk:install @@ -72,12 +73,13 @@ php artisan dusk:install Next, set the `APP_URL` environment variable in your application's `.env` file. This value should match the URL you use to access your application in a browser. -> {tip} If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). +> **Note** +> If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please also consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). ### Managing ChromeDriver Installations -If you would like to install a different version of ChromeDriver than what is included with Laravel Dusk, you may use the `dusk:chrome-driver` command: +If you would like to install a different version of ChromeDriver than what is installed by Laravel Dusk via the `dusk:install` command, you may use the `dusk:chrome-driver` command: ```shell # Install the latest version of ChromeDriver for your OS... @@ -93,7 +95,8 @@ php artisan dusk:chrome-driver --all php artisan dusk:chrome-driver --detect ``` -> {note} Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. +> **Warning** +> Dusk requires the `chromedriver` binaries to be executable. If you're having problems running Dusk, you should ensure the binaries are executable using the following command: `chmod -R 0755 vendor/laravel/dusk/bin/`. ### Using Other Browsers @@ -158,7 +161,8 @@ Most of the tests you write will interact with pages that retrieve data from you use DatabaseMigrations; } -> {note} SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. +> **Warning** +> SQLite in-memory databases may not be used when executing Dusk tests. Since the browser executes within its own process, it will not be able to access the in-memory databases of other processes. ### Running Tests @@ -181,7 +185,8 @@ The `dusk` command accepts any argument that is normally accepted by the PHPUnit php artisan dusk --group=foo ``` -> {tip} If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). +> **Note** +> If you are using [Laravel Sail](/docs/{{version}}/sail) to manage your local development environment, please consult the Sail documentation on [configuring and running Dusk tests](/docs/{{version}}/sail#laravel-dusk). #### Manually Starting ChromeDriver @@ -377,7 +382,8 @@ Often, you will be testing pages that require authentication. You can use Dusk's ->visit('/home'); }); -> {note} After using the `loginAs` method, the user session will be maintained for all tests within the file. +> **Warning** +> After using the `loginAs` method, the user session will be maintained for all tests within the file. ### Cookies @@ -419,6 +425,10 @@ You may use the `screenshot` method to take a screenshot and store it with the g $browser->screenshot('filename'); +The `responsiveScreenshots` method may be used to take a series of screenshots at various breakpoints: + + $browser->responsiveScreenshots('filename'); + ### Storing Console Output To Disk @@ -563,7 +573,8 @@ The `attach` method may be used to attach a file to a `file` input element. Like $browser->attach('photo', __DIR__.'/photos/mountains.png'); -> {note} The attach function requires the `Zip` PHP extension to be installed and enabled on your server. +> **Warning** +> The attach function requires the `Zip` PHP extension to be installed and enabled on your server. ### Pressing Buttons @@ -572,7 +583,7 @@ The `press` method may be used to click a button element on the page. The argume $browser->press('Login'); -When submitting forms, many application's disable the form's submission button after it is pressed and then re-enable the button when the form submission's HTTP request is complete. To press a button and wait for the button to be re-enabled, you may use the `pressAndWaitFor` method: +When submitting forms, many applications disable the form's submission button after it is pressed and then re-enable the button when the form submission's HTTP request is complete. To press a button and wait for the button to be re-enabled, you may use the `pressAndWaitFor` method: // Press the button and wait a maximum of 5 seconds for it to be enabled... $browser->pressAndWaitFor('Save'); @@ -593,7 +604,8 @@ You may use the `seeLink` method to determine if a link with the given display t // ... } -> {note} These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. +> **Warning** +> These methods interact with jQuery. If jQuery is not available on the page, Dusk will automatically inject it into the page so it is available for the test's duration. ### Using The Keyboard @@ -606,7 +618,8 @@ Another valuable use case for the `keys` method is sending a "keyboard shortcut" $browser->keys('.app', ['{command}', 'j']); -> {tip} All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). +> **Note** +> All modifier keys such as `{command}` are wrapped in `{}` characters, and match the constants defined in the `Facebook\WebDriver\WebDriverKeys` class, which can be [found on GitHub](https://github.com/php-webdriver/php-webdriver/blob/master/lib/WebDriverKeys.php). ### Using The Mouse @@ -1817,7 +1830,8 @@ Once the component has been defined, we can easily select a date within the date ## Continuous Integration -> {note} Most Dusk continuous integration configurations expect your Laravel application to be served using the built-in PHP development server on port 8000. Therefore, before continuing, you should ensure that your continuous integration environment has an `APP_URL` environment variable value of `http://127.0.0.1:8000`. +> **Warning** +> Most Dusk continuous integration configurations expect your Laravel application to be served using the built-in PHP development server on port 8000. Therefore, before continuing, you should ensure that your continuous integration environment has an `APP_URL` environment variable value of `http://127.0.0.1:8000`. ### Heroku CI @@ -1870,7 +1884,7 @@ script: ### GitHub Actions -If you are using [Github Actions](https://github.com/features/actions) to run your Dusk tests, you may use the following configuration file as a starting point. Like TravisCI, we will use the `php artisan serve` command to launch PHP's built-in web server: +If you are using [GitHub Actions](https://github.com/features/actions) to run your Dusk tests, you may use the following configuration file as a starting point. Like TravisCI, we will use the `php artisan serve` command to launch PHP's built-in web server: ```yaml name: CI @@ -1880,13 +1894,13 @@ jobs: dusk-php: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Prepare The Environment run: cp .env.example .env - name: Create Database run: | sudo systemctl start mysql - mysql --user="root" --password="root" -e "CREATE DATABASE 'my-database' character set UTF8mb4 collate utf8mb4_bin;" + mysql --user="root" --password="root" -e "CREATE DATABASE \`my-database\` character set UTF8mb4 collate utf8mb4_bin;" - name: Install Composer Dependencies run: composer install --no-progress --prefer-dist --optimize-autoloader - name: Generate Application Key diff --git a/eloquent-collections.md b/eloquent-collections.md index d60839b3c48..00adcc34ab6 100644 --- a/eloquent-collections.md +++ b/eloquent-collections.md @@ -62,6 +62,7 @@ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a supe
+[append](#method-append) [contains](#method-contains) [diff](#method-diff) [except](#method-except) @@ -79,8 +80,17 @@ In addition, the `Illuminate\Database\Eloquent\Collection` class provides a supe
+ +#### `append($attributes)` {.collection-method .first-collection-method} + +The `append` method may be used to indicate that an attribute should be [appended](/docs/{{version}}/eloquent-serialization#appending-values-to-json) for every model in the collection. This method accepts an array of attributes or a single attribute: + + $users->append('team'); + + $users->append(['team', 'is_admin']); + -#### `contains($key, $operator = null, $value = null)` {.collection-method .first-collection-method} +#### `contains($key, $operator = null, $value = null)` {.collection-method} The `contains` method may be used to determine if a given model instance is contained by the collection. This method accepts a primary key or a model instance: diff --git a/eloquent-factories.md b/eloquent-factories.md new file mode 100644 index 00000000000..02c9e4973c0 --- /dev/null +++ b/eloquent-factories.md @@ -0,0 +1,489 @@ +# Eloquent: Factories + +- [Introduction](#introduction) +- [Defining Model Factories](#defining-model-factories) + - [Generating Factories](#generating-factories) + - [Factory States](#factory-states) + - [Factory Callbacks](#factory-callbacks) +- [Creating Models Using Factories](#creating-models-using-factories) + - [Instantiating Models](#instantiating-models) + - [Persisting Models](#persisting-models) + - [Sequences](#sequences) +- [Factory Relationships](#factory-relationships) + - [Has Many Relationships](#has-many-relationships) + - [Belongs To Relationships](#belongs-to-relationships) + - [Many To Many Relationships](#many-to-many-relationships) + - [Polymorphic Relationships](#polymorphic-relationships) + - [Defining Relationships Within Factories](#defining-relationships-within-factories) + + +## Introduction + +When testing your application or seeding your database, you may need to insert a few records into your database. Instead of manually specifying the value of each column, Laravel allows you to define a set of default attributes for each of your [Eloquent models](/docs/{{version}}/eloquent) using model factories. + +To see an example of how to write a factory, take a look at the `database/factories/UserFactory.php` file in your application. This factory is included with all new Laravel applications and contains the following factory definition: + + namespace Database\Factories; + + use Illuminate\Database\Eloquent\Factories\Factory; + use Illuminate\Support\Str; + + class UserFactory extends Factory + { + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'remember_token' => Str::random(10), + ]; + } + } + +As you can see, in their most basic form, factories are classes that extend Laravel's base factory class and define a `definition` method. The `definition` method returns the default set of attribute values that should be applied when creating a model using the factory. + +Via the `fake` helper, factories have access to the [Faker](https://github.com/FakerPHP/Faker) PHP library, which allows you to conveniently generate various kinds of random data for testing and seeding. + +> **Note** +> You can set your application's Faker locale by adding a `faker_locale` option to your `config/app.php` configuration file. + + +## Defining Model Factories + + +### Generating Factories + +To create a factory, execute the `make:factory` [Artisan command](/docs/{{version}}/artisan): + +```shell +php artisan make:factory PostFactory +``` + +The new factory class will be placed in your `database/factories` directory. + + +#### Model & Factory Discovery Conventions + +Once you have defined your factories, you may use the static `factory` method provided to your models by the `Illuminate\Database\Eloquent\Factories\HasFactory` trait in order to instantiate a factory instance for that model. + +The `HasFactory` trait's `factory` method will use conventions to determine the proper factory for the model the trait is assigned to. Specifically, the method will look for a factory in the `Database\Factories` namespace that has a class name matching the model name and is suffixed with `Factory`. If these conventions do not apply to your particular application or factory, you may overwrite the `newFactory` method on your model to return an instance of the model's corresponding factory directly: + + use Database\Factories\Administration\FlightFactory; + + /** + * Create a new factory instance for the model. + * + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + protected static function newFactory() + { + return FlightFactory::new(); + } + +Next, define a `model` property on the corresponding factory: + + use App\Administration\Flight; + use Illuminate\Database\Eloquent\Factories\Factory; + + class FlightFactory extends Factory + { + /** + * The name of the factory's corresponding model. + * + * @var string + */ + protected $model = Flight::class; + } + + +### Factory States + +State manipulation methods allow you to define discrete modifications that can be applied to your model factories in any combination. For example, your `Database\Factories\UserFactory` factory might contain a `suspended` state method that modifies one of its default attribute values. + +State transformation methods typically call the `state` method provided by Laravel's base factory class. The `state` method accepts a closure which will receive the array of raw attributes defined for the factory and should return an array of attributes to modify: + + /** + * Indicate that the user is suspended. + * + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + public function suspended() + { + return $this->state(function (array $attributes) { + return [ + 'account_status' => 'suspended', + ]; + }); + } + +#### "Trashed" State + +If your Eloquent model can be [soft deleted](/docs/{{version}}/eloquent#soft-deleting), you may invoke the built-in `trashed` state method to indicate that the created model should already be "soft deleted". You do not need to manually define the `trashed` state as it is automatically available to all factories: + + use App\Models\User; + + $user = User::factory()->trashed()->create(); + + +### Factory Callbacks + +Factory callbacks are registered using the `afterMaking` and `afterCreating` methods and allow you to perform additional tasks after making or creating a model. You should register these callbacks by defining a `configure` method on your factory class. This method will be automatically called by Laravel when the factory is instantiated: + + namespace Database\Factories; + + use App\Models\User; + use Illuminate\Database\Eloquent\Factories\Factory; + use Illuminate\Support\Str; + + class UserFactory extends Factory + { + /** + * Configure the model factory. + * + * @return $this + */ + public function configure() + { + return $this->afterMaking(function (User $user) { + // + })->afterCreating(function (User $user) { + // + }); + } + + // ... + } + + +## Creating Models Using Factories + + +### Instantiating Models + +Once you have defined your factories, you may use the static `factory` method provided to your models by the `Illuminate\Database\Eloquent\Factories\HasFactory` trait in order to instantiate a factory instance for that model. Let's take a look at a few examples of creating models. First, we'll use the `make` method to create models without persisting them to the database: + + use App\Models\User; + + $user = User::factory()->make(); + +You may create a collection of many models using the `count` method: + + $users = User::factory()->count(3)->make(); + + +#### Applying States + +You may also apply any of your [states](#factory-states) to the models. If you would like to apply multiple state transformations to the models, you may simply call the state transformation methods directly: + + $users = User::factory()->count(5)->suspended()->make(); + + +#### Overriding Attributes + +If you would like to override some of the default values of your models, you may pass an array of values to the `make` method. Only the specified attributes will be replaced while the rest of the attributes remain set to their default values as specified by the factory: + + $user = User::factory()->make([ + 'name' => 'Abigail Otwell', + ]); + +Alternatively, the `state` method may be called directly on the factory instance to perform an inline state transformation: + + $user = User::factory()->state([ + 'name' => 'Abigail Otwell', + ])->make(); + +> **Note** +> [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled when creating models using factories. + + +### Persisting Models + +The `create` method instantiates model instances and persists them to the database using Eloquent's `save` method: + + use App\Models\User; + + // Create a single App\Models\User instance... + $user = User::factory()->create(); + + // Create three App\Models\User instances... + $users = User::factory()->count(3)->create(); + +You may override the factory's default model attributes by passing an array of attributes to the `create` method: + + $user = User::factory()->create([ + 'name' => 'Abigail', + ]); + + +### Sequences + +Sometimes you may wish to alternate the value of a given model attribute for each created model. You may accomplish this by defining a state transformation as a sequence. For example, you may wish to alternate the value of an `admin` column between `Y` and `N` for each created user: + + use App\Models\User; + use Illuminate\Database\Eloquent\Factories\Sequence; + + $users = User::factory() + ->count(10) + ->state(new Sequence( + ['admin' => 'Y'], + ['admin' => 'N'], + )) + ->create(); + +In this example, five users will be created with an `admin` value of `Y` and five users will be created with an `admin` value of `N`. + +If necessary, you may include a closure as a sequence value. The closure will be invoked each time the sequence needs a new value: + + $users = User::factory() + ->count(10) + ->state(new Sequence( + fn ($sequence) => ['role' => UserRoles::all()->random()], + )) + ->create(); + +Within a sequence closure, you may access the `$index` or `$count` properties on the sequence instance that is injected into the closure. The `$index` property contains the number of iterations through the sequence that have occurred thus far, while the `$count` property contains the total number of times the sequence will be invoked: + + $users = User::factory() + ->count(10) + ->sequence(fn ($sequence) => ['name' => 'Name '.$sequence->index]) + ->create(); + + +## Factory Relationships + + +### Has Many Relationships + +Next, let's explore building Eloquent model relationships using Laravel's fluent factory methods. First, let's assume our application has an `App\Models\User` model and an `App\Models\Post` model. Also, let's assume that the `User` model defines a `hasMany` relationship with `Post`. We can create a user that has three posts using the `has` method provided by the Laravel's factories. The `has` method accepts a factory instance: + + use App\Models\Post; + use App\Models\User; + + $user = User::factory() + ->has(Post::factory()->count(3)) + ->create(); + +By convention, when passing a `Post` model to the `has` method, Laravel will assume that the `User` model must have a `posts` method that defines the relationship. If necessary, you may explicitly specify the name of the relationship that you would like to manipulate: + + $user = User::factory() + ->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: + + $user = User::factory() + ->has( + Post::factory() + ->count(3) + ->state(function (array $attributes, User $user) { + return ['user_type' => $user->type]; + }) + ) + ->create(); + + +#### Using Magic Methods + +For convenience, you may use Laravel's magic factory relationship methods to build relationships. For example, the following example will use convention to determine that the related models should be created via a `posts` relationship method on the `User` model: + + $user = User::factory() + ->hasPosts(3) + ->create(); + +When using magic methods to create factory relationships, you may pass an array of attributes to override on the related models: + + $user = User::factory() + ->hasPosts(3, [ + 'published' => false, + ]) + ->create(); + +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) { + return ['user_type' => $user->type]; + }) + ->create(); + + +### Belongs To Relationships + +Now that we have explored how to build "has many" relationships using factories, let's explore the inverse of the relationship. The `for` method may be used to define the parent model that factory created models belong to. For example, we can create three `App\Models\Post` model instances that belong to a single user: + + use App\Models\Post; + use App\Models\User; + + $posts = Post::factory() + ->count(3) + ->for(User::factory()->state([ + 'name' => 'Jessica Archer', + ])) + ->create(); + +If you already have a parent model instance that should be associated with the models you are creating, you may pass the model instance to the `for` method: + + $user = User::factory()->create(); + + $posts = Post::factory() + ->count(3) + ->for($user) + ->create(); + + +#### Using Magic Methods + +For convenience, you may use Laravel's magic factory relationship methods to define "belongs to" relationships. For example, the following example will use convention to determine that the three posts should belong to the `user` relationship on the `Post` model: + + $posts = Post::factory() + ->count(3) + ->forUser([ + 'name' => 'Jessica Archer', + ]) + ->create(); + + +### Many To Many Relationships + +Like [has many relationships](#has-many-relationships), "many to many" relationships may be created using the `has` method: + + use App\Models\Role; + use App\Models\User; + + $user = User::factory() + ->has(Role::factory()->count(3)) + ->create(); + + +#### Pivot Table Attributes + +If you need to define attributes that should be set on the pivot / intermediate table linking the models, you may use the `hasAttached` method. This method accepts an array of pivot table attribute names and values as its second argument: + + use App\Models\Role; + use App\Models\User; + + $user = User::factory() + ->hasAttached( + Role::factory()->count(3), + ['active' => true] + ) + ->create(); + +You may provide a closure based state transformation if your state change requires access to the related model: + + $user = User::factory() + ->hasAttached( + Role::factory() + ->count(3) + ->state(function (array $attributes, User $user) { + return ['name' => $user->name.' Role']; + }), + ['active' => true] + ) + ->create(); + +If you already have model instances that you would like to be attached to the models you are creating, you may pass the model instances to the `hasAttached` method. In this example, the same three roles will be attached to all three users: + + $roles = Role::factory()->count(3)->create(); + + $user = User::factory() + ->count(3) + ->hasAttached($roles, ['active' => true]) + ->create(); + + +#### Using Magic Methods + +For convenience, you may use Laravel's magic factory relationship methods to define many to many relationships. For example, the following example will use convention to determine that the related models should be created via a `roles` relationship method on the `User` model: + + $user = User::factory() + ->hasRoles(1, [ + 'name' => 'Editor' + ]) + ->create(); + + +### Polymorphic Relationships + +[Polymorphic relationships](/docs/{{version}}/eloquent-relationships#polymorphic-relationships) may also be created using factories. Polymorphic "morph many" relationships are created in the same way as typical "has many" relationships. For example, if a `App\Models\Post` model has a `morphMany` relationship with a `App\Models\Comment` model: + + use App\Models\Post; + + $post = Post::factory()->hasComments(3)->create(); + + +#### Morph To Relationships + +Magic methods may not be used to create `morphTo` relationships. Instead, the `for` method must be used directly and the name of the relationship must be explicitly provided. For example, imagine that the `Comment` model has a `commentable` method that defines a `morphTo` relationship. In this situation, we may create three comments that belong to a single post by using the `for` method directly: + + $comments = Comment::factory()->count(3)->for( + Post::factory(), 'commentable' + )->create(); + + +#### Polymorphic Many To Many Relationships + +Polymorphic "many to many" (`morphToMany` / `morphedByMany`) relationships may be created just like non-polymorphic "many to many" relationships: + + use App\Models\Tag; + use App\Models\Video; + + $videos = Video::factory() + ->hasAttached( + Tag::factory()->count(3), + ['public' => true] + ) + ->create(); + +Of course, the magic `has` method may also be used to create polymorphic "many to many" relationships: + + $videos = Video::factory() + ->hasTags(3, ['public' => true]) + ->create(); + + +### Defining Relationships Within Factories + +To define a relationship within your model factory, you will typically assign a new factory instance to the foreign key of the relationship. This is normally done for the "inverse" relationships such as `belongsTo` and `morphTo` relationships. For example, if you would like to create a new user when creating a post, you may do the following: + + use App\Models\User; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'user_id' => User::factory(), + 'title' => fake()->title(), + 'content' => fake()->paragraph(), + ]; + } + +If the relationship's columns depend on the factory that defines it you may assign a closure to an attribute. The closure will receive the factory's evaluated attribute array: + + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'user_id' => User::factory(), + 'user_type' => function (array $attributes) { + return User::find($attributes['user_id'])->type; + }, + 'title' => fake()->title(), + 'content' => fake()->paragraph(), + ]; + } diff --git a/eloquent-mutators.md b/eloquent-mutators.md index 04c86b23911..c9413f15cda 100644 --- a/eloquent-mutators.md +++ b/eloquent-mutators.md @@ -64,7 +64,8 @@ As you can see, the original value of the column is passed to the accessor, allo $firstName = $user->first_name; -> {tip} If you would like these computed values to be added to the array / JSON representations of your model, [you will need to append them](/docs/{{version}}/eloquent-serialization#appending-values-to-json). +> **Note** +> If you would like these computed values to be added to the array / JSON representations of your model, [you will need to append them](/docs/{{version}}/eloquent-serialization#appending-values-to-json). #### Building Value Objects From Multiple Attributes @@ -80,7 +81,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute; * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ -public function address(): Attribute +protected function address(): Attribute { return Attribute::make( get: fn ($value, $attributes) => new Address( @@ -108,7 +109,7 @@ When returning value objects from accessors, any changes made to the value objec However, you may sometimes wish to enable caching for primitive values like strings and booleans, particularly if they are computationally intensive. To accomplish this, you may invoke the `shouldCache` method when defining your accessor: ```php -public function hash(): Attribute +protected function hash(): Attribute { return Attribute::make( get: fn ($value) => bcrypt(gzuncompress($value)), @@ -124,7 +125,7 @@ If you would like to disable the object caching behavior of attributes, you may * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ -public function address(): Attribute +protected function address(): Attribute { return Attribute::make( get: fn ($value, $attributes) => new Address( @@ -152,7 +153,6 @@ A mutator transforms an Eloquent attribute value when it is set. To define a mut /** * Interact with the user's first name. * - * @param string $value * @return \Illuminate\Database\Eloquent\Casts\Attribute */ protected function firstName(): Attribute @@ -172,7 +172,7 @@ The mutator closure will receive the value that is being set on the attribute, a $user->first_name = 'Sally'; -In this example, the `set` callback will be called with the value `Sally`. The mutator will then apply the `strtolower` function to the name and set its resulting value in model's the internal `$attributes` array. +In this example, the `set` callback will be called with the value `Sally`. The mutator will then apply the `strtolower` function to the name and set its resulting value in the model's internal `$attributes` array. #### Mutating Multiple Attributes @@ -188,7 +188,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute; * * @return \Illuminate\Database\Eloquent\Casts\Attribute */ -public function address(): Attribute +protected function address(): Attribute { return Attribute::make( get: fn ($value, $attributes) => new Address( @@ -220,7 +220,7 @@ The `$casts` property should be an array where the key is the name of the attrib - `datetime` - `immutable_date` - `immutable_datetime` -- `decimal:`<digits> +- decimal:<precision> - `double` - `encrypted` - `encrypted:array` @@ -270,7 +270,8 @@ If you need to add a new, temporary cast at runtime, you may use the `mergeCasts 'options' => 'object', ]); -> {note} Attributes that are `null` will not be cast. In addition, you should never define a cast (or an attribute) that has the same name as a relationship. +> **Warning** +> Attributes that are `null` will not be cast. In addition, you should never define a cast (or an attribute) that has the same name as a relationship. #### Stringable Casting @@ -424,9 +425,10 @@ If a custom format is applied to the `date` or `datetime` cast, such as `datetim ### Enum Casting -> {note} Enum casting is only available for PHP 8.1+. +> **Warning** +> Enum casting is only available for PHP 8.1+. -Eloquent also allows you to cast your attribute values to PHP ["backed" Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: +Eloquent also allows you to cast your attribute values to PHP [Enums](https://www.php.net/manual/en/language.enumerations.backed.php). To accomplish this, you may specify the attribute and enum you wish to cast in your model's `$casts` property array: use App\Enums\ServerStatus; @@ -559,7 +561,7 @@ As an example, we will define a custom cast class that casts multiple model valu namespace App\Casts; - use App\Models\Address as AddressModel; + use App\ValueObjects\Address as AddressValueObject; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use InvalidArgumentException; @@ -572,11 +574,11 @@ As an example, we will define a custom cast class that casts multiple model valu * @param string $key * @param mixed $value * @param array $attributes - * @return \App\Models\Address + * @return \App\ValueObjects\Address */ public function get($model, $key, $value, $attributes) { - return new AddressModel( + return new AddressValueObject( $attributes['address_line_one'], $attributes['address_line_two'] ); @@ -587,13 +589,13 @@ As an example, we will define a custom cast class that casts multiple model valu * * @param \Illuminate\Database\Eloquent\Model $model * @param string $key - * @param \App\Models\Address $value + * @param \App\ValueObjects\Address $value * @param array $attributes * @return array */ public function set($model, $key, $value, $attributes) { - if (! $value instanceof AddressModel) { + if (! $value instanceof AddressValueObject) { throw new InvalidArgumentException('The given value is not an Address instance.'); } @@ -614,7 +616,8 @@ When casting to value objects, any changes made to the value object will automat $user->save(); -> {tip} If you plan to serialize your Eloquent models containing value objects to JSON or arrays, you should implement the `Illuminate\Contracts\Support\Arrayable` and `JsonSerializable` interfaces on the value object. +> **Note** +> If you plan to serialize your Eloquent models containing value objects to JSON or arrays, you should implement the `Illuminate\Contracts\Support\Arrayable` and `JsonSerializable` interfaces on the value object. ### Array / JSON Serialization diff --git a/eloquent-relationships.md b/eloquent-relationships.md index a1053bd74e1..45584935c7c 100644 --- a/eloquent-relationships.md +++ b/eloquent-relationships.md @@ -343,7 +343,8 @@ public function largestOrder() } ``` -> {note} Because PostgreSQL does not support executing the `MAX` function against UUID columns, it is not currently possible to use one-of-many relationships in combination with PostgreSQL UUID columns. +> **Warning** +> Because PostgreSQL does not support executing the `MAX` function against UUID columns, it is not currently possible to use one-of-many relationships in combination with PostgreSQL UUID columns. #### Advanced Has One Of Many Relationships @@ -609,7 +610,8 @@ If you would like your intermediate table to have `created_at` and `updated_at` return $this->belongsToMany(Role::class)->withTimestamps(); -> {note} Intermediate tables that utilize Eloquent's automatically maintained timestamps are required to have both `created_at` and `updated_at` timestamp columns. +> **Warning** +> 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 @@ -697,7 +699,8 @@ When defining the `RoleUser` model, you should extend the `Illuminate\Database\E // } -> {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. +> **Warning** +> 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 @@ -950,7 +953,8 @@ public function bestImage() } ``` -> {tip} It is possible to construct more advanced "one of many" relationships. For more information, please consult the [has one of many documentation](#advanced-has-one-of-many-relationships). +> **Note** +> It is possible to construct more advanced "one of many" relationships. For more information, please consult the [has one of many documentation](#advanced-has-one-of-many-relationships). ### Many To Many (Polymorphic) @@ -977,7 +981,8 @@ Many-to-many polymorphic relations are slightly more complicated than "morph one 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). +> **Note** +> 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 @@ -1086,7 +1091,8 @@ You may determine the morph alias of a given model at runtime using the model's $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. +> **Warning** +> 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 @@ -1102,7 +1108,8 @@ The `resolveRelationUsing` method accepts the desired relationship name as its f return $orderModel->belongsTo(Customer::class, 'customer_id'); }); -> {note} When defining dynamic relationships, always provide explicit key name arguments to the Eloquent relationship methods. +> **Warning** +> When defining dynamic relationships, always provide explicit key name arguments to the Eloquent relationship methods. ## Querying Relations @@ -1224,12 +1231,13 @@ If you need even more power, you may use the `whereHas` and `orWhereHas` methods $query->where('content', 'like', 'code%'); }, '>=', 10)->get(); -> {note} Eloquent does not currently support querying for relationship existence across databases. The relationships must exist within the same database. +> **Warning** +> Eloquent does not currently support querying for relationship existence across databases. The relationships must exist within the same database. #### Inline Relationship Existence Queries -If you would like to query for a relationship's existence with a single, simple where condition attached to the relationship query, you may find it more convenient to use the `whereRelation` and `whereMorphRelation` methods. For example, we may query for all posts that have unapproved comments: +If you would like to query for a relationship's existence with a single, simple where condition attached to the relationship query, you may find it more convenient to use the `whereRelation`, `orWhereRelation`, `whereMorphRelation`, and `orWhereMorphRelation` methods. For example, we may query for all posts that have unapproved comments: use App\Models\Post; @@ -1512,6 +1520,15 @@ To eager load a relationship's relationships, you may use "dot" syntax. For exam $books = Book::with('author.contacts')->get(); +Alternatively, you may specify nested eager loaded relationships by providing a nested array to the `with` method, which can be convenient when eager loading multiple nested relationships: + + $books = Book::with([ + 'author' => [ + 'contacts', + 'publisher', + ], + ])->get(); + #### Nested Eager Loading `morphTo` Relationships @@ -1554,7 +1571,8 @@ You may not always need every column from the relationships you are retrieving. $books = Book::with('author:id,name,book_id')->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. +> **Warning** +> 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. #### Eager Loading By Default @@ -1618,7 +1636,8 @@ In this example, Eloquent will only eager load posts where the post's `title` co $query->orderBy('created_at', 'desc'); }])->get(); -> {note} The `limit` and `take` query builder methods may not be used when constraining eager loads. +> **Warning** +> The `limit` and `take` query builder methods may not be used when constraining eager loads. #### Constraining Eager Loading Of `morphTo` Relationships @@ -1641,6 +1660,17 @@ If you are eager loading a `morphTo` relationship, Eloquent will run multiple qu In this example, Eloquent will only eager load posts that have not been hidden and videos that have a `type` value of "educational". + +#### Constraining Eager Loads With Relationship Existence + +You may sometimes find yourself needing to check for the existence of a relationship while simultaneously loading the relationship based on the same conditions. For example, you may wish to only retrieve `User` models that have child `Post` models matching a given query condition while also eager loading the matching posts. You may accomplish this using the `withWhereHas` method: + + use App\Models\User; + + $users = User::withWhereHas('posts', function ($query) { + $query->where('featured', true); + })->get(); + ### Lazy Eager Loading @@ -1804,7 +1834,8 @@ You may use the `createMany` method to create multiple related models: You may also use the `findOrNew`, `firstOrNew`, `firstOrCreate`, and `updateOrCreate` methods to [create and update models on relationships](/docs/{{version}}/eloquent#upserts). -> {tip} Before using the `create` method, be sure to review the [mass assignment](/docs/{{version}}/eloquent#mass-assignment) documentation. +> **Note** +> Before using the `create` method, be sure to review the [mass assignment](/docs/{{version}}/eloquent#mass-assignment) documentation. ### Belongs To Relationships @@ -1930,4 +1961,5 @@ For example, when a `Comment` model is updated, you may want to automatically "t } } -> {note} Parent model timestamps will only be updated if the child model is updated using Eloquent's `save` method. +> **Warning** +> Parent model timestamps will only be updated if the child model is updated using Eloquent's `save` method. diff --git a/eloquent-resources.md b/eloquent-resources.md index a9c5a73d9e2..978d7fb6737 100644 --- a/eloquent-resources.md +++ b/eloquent-resources.md @@ -44,7 +44,8 @@ php artisan make:resource UserCollection ## Concept Overview -> {tip} This is a high-level overview of resources and resource collections. You are highly encouraged to read the other sections of this documentation to gain a deeper understanding of the customization and power offered to you by resources. +> **Note** +> This is a high-level overview of resources and resource collections. You are highly encouraged to read the other sections of this documentation to gain a deeper understanding of the customization and power offered to you by resources. Before diving into all of the options available to you when writing resources, let's first take a high-level look at how resources are used within Laravel. A resource class represents a single model that needs to be transformed into a JSON structure. For example, here is a simple `UserResource` resource class: @@ -195,7 +196,8 @@ For example, `UserCollection` will attempt to map the given user instances into ## Writing Resources -> {tip} If you have not read the [concept overview](#concept-overview), you are highly encouraged to do so before proceeding with this documentation. +> **Note** +> If you have not read the [concept overview](#concept-overview), you are highly encouraged to do so before proceeding with this documentation. In essence, resources are simple. They only need to transform a given model into an array. So, each resource contains a `toArray` method which translates your model's attributes into an API friendly array that can be returned from your application's routes or controllers: @@ -259,7 +261,8 @@ If you would like to include related resources in your response, you may add the ]; } -> {tip} If you would like to include relationships only when they have already been loaded, check out the documentation on [conditional relationships](#conditional-relationships). +> **Note** +> If you would like to include relationships only when they have already been loaded, check out the documentation on [conditional relationships](#conditional-relationships). #### Resource Collections @@ -320,12 +323,12 @@ By default, your outermost resource is wrapped in a `data` key when the resource { "id": 1, "name": "Eladio Schroeder Sr.", - "email": "therese28@example.com", + "email": "therese28@example.com" }, { "id": 2, "name": "Liliana Mayert", - "email": "evandervort@example.com", + "email": "evandervort@example.com" } ] } @@ -344,7 +347,7 @@ If you would like to use a custom key instead of `data`, you may define a `$wrap /** * The "data" wrapper that should be applied. * - * @var string + * @var string|null */ public static $wrap = 'user'; } @@ -381,7 +384,8 @@ If you would like to disable the wrapping of the outermost resource, you should } } -> {note} The `withoutWrapping` method only affects the outermost response and will not remove `data` keys that you manually add to your own resource collections. +> **Warning** +> The `withoutWrapping` method only affects the outermost response and will not remove `data` keys that you manually add to your own resource collections. #### Wrapping Nested Resources @@ -421,12 +425,12 @@ When returning paginated collections via a resource response, Laravel will wrap { "id": 1, "name": "Eladio Schroeder Sr.", - "email": "therese28@example.com", + "email": "therese28@example.com" }, { "id": 2, "name": "Liliana Mayert", - "email": "evandervort@example.com", + "email": "evandervort@example.com" } ], "links":{ @@ -467,12 +471,12 @@ Paginated responses always contain `meta` and `links` keys with information abou { "id": 1, "name": "Eladio Schroeder Sr.", - "email": "therese28@example.com", + "email": "therese28@example.com" }, { "id": 2, "name": "Liliana Mayert", - "email": "evandervort@example.com", + "email": "evandervort@example.com" } ], "links":{ @@ -556,7 +560,8 @@ Sometimes you may have several attributes that should only be included in the re Again, if the given condition is `false`, these attributes will be removed from the resource response before it is sent to the client. -> {note} The `mergeWhen` method should not be used within arrays that mix string and numeric keys. Furthermore, it should not be used within arrays with numeric keys that are not ordered sequentially. +> **Warning** +> The `mergeWhen` method should not be used within arrays that mix string and numeric keys. Furthermore, it should not be used within arrays with numeric keys that are not ordered sequentially. ### Conditional Relationships @@ -587,6 +592,35 @@ The `whenLoaded` method may be used to conditionally load a relationship. In ord In this example, if the relationship has not been loaded, the `posts` key will be removed from the resource response before it is sent to the client. + +#### Conditional Relationship Counts + +In addition to conditionally including relationships, you may conditionally include relationship "counts" on your resource responses based on if the relationship's count has been loaded on the model: + + new UserResource($user->loadCount('posts')); + +The `whenCounted` method may be used to conditionally include a relationship's count in your resource response. This method avoids unnecessarily including the attribute if the relationships' count is not present: + + /** + * Transform the resource into an array. + * + * @param \Illuminate\Http\Request $request + * @return array + */ + public function toArray($request) + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'posts_count' => $this->whenCounted('posts'), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } + +In this example, if the `posts` relationship's count has not been loaded, the `posts_count` key will be removed from the resource response before it is sent to the client. + #### Conditional Pivot Information diff --git a/eloquent-serialization.md b/eloquent-serialization.md index e90918ae154..d1fabbef9d6 100644 --- a/eloquent-serialization.md +++ b/eloquent-serialization.md @@ -13,7 +13,8 @@ When building APIs using Laravel, you will often need to convert your models and relationships to arrays or JSON. Eloquent includes convenient methods for making these conversions, as well as controlling which attributes are included in the serialized representation of your models. -> {tip} For an even more robust way of handling Eloquent model and collection JSON serialization, check out the documentation on [Eloquent API resources](/docs/{{version}}/eloquent-resources). +> **Note** +> For an even more robust way of handling Eloquent model and collection JSON serialization, check out the documentation on [Eloquent API resources](/docs/{{version}}/eloquent-resources). ## Serializing Models & Collections @@ -90,7 +91,8 @@ Sometimes you may wish to limit the attributes, such as passwords, that are incl protected $hidden = ['password']; } -> {tip} To hide relationships, add the relationship's method name to your Eloquent model's `$hidden` property. +> **Note** +> To hide relationships, add the relationship's method name to your Eloquent model's `$hidden` property. Alternatively, you may use the `visible` property to define an "allow list" of attributes that should be included in your model's array and JSON representation. All attributes that are not present in the `$visible` array will be hidden when the model is converted to an array or JSON: diff --git a/eloquent.md b/eloquent.md index 67fca1ca55c..b0630d6a1c2 100644 --- a/eloquent.md +++ b/eloquent.md @@ -41,7 +41,8 @@ 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. -> {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). +> **Note** +> 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). ## Generating Model Classes @@ -90,6 +91,15 @@ php artisan make:model Flight --all php artisan make:model Member --pivot ``` + +#### Inspecting Models + +Sometimes it can be difficult to determine all of a model's available attributes and relationships just by skimming its code. Instead, try the `model:show` Artisan command, which provides a convenient overview of all the model's attributes and relations: + +```shell +php artisan model:show Flight +``` + ## Eloquent Model Conventions @@ -297,7 +307,8 @@ The Eloquent `all` method will return all of the results in the model's table. H ->take(10) ->get(); -> {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. +> **Note** +> 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 @@ -401,7 +412,8 @@ Similar to the `lazy` method, the `cursor` method may be used to significantly r 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. -> {note} Since the `cursor` method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load relationships, consider using [the `lazy` method](#chunking-using-lazy-collections) instead. +> **Warning** +> Since the `cursor` method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load relationships, consider using [the `lazy` method](#chunking-using-lazy-collections) instead. Internally, the `cursor` method uses PHP [generators](https://www.php.net/manual/en/language.generators.overview.php) to implement this functionality: @@ -615,7 +627,8 @@ Updates can also be performed against models that match a given query. In this e The `update` method expects an array of column and value pairs representing the columns that should be updated. The `update` method returns the number of affected rows. -> {note} When issuing a mass update via Eloquent, the `saving`, `saved`, `updating`, and `updated` model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update. +> **Warning** +> When issuing a mass update via Eloquent, the `saving`, `saved`, `updating`, and `updated` model events will not be fired for the updated models. This is because the models are never actually retrieved when issuing a mass update. #### Examining Attribute Changes @@ -766,7 +779,8 @@ If you would like to perform multiple "upserts" in a single query, then you shou ['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. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. +> **Warning** +> All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. ## Deleting Models @@ -796,7 +810,8 @@ In the example above, we are retrieving the model from the database before calli Flight::destroy(collect([1, 2, 3])); -> {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. +> **Warning** +> 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 Using Queries @@ -805,7 +820,8 @@ Of course, you may build an Eloquent query to delete all models matching your qu $deleted = Flight::where('active', 0)->delete(); -> {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. +> **Warning** +> 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 @@ -824,7 +840,8 @@ In addition to actually removing records from your database, Eloquent can also " use SoftDeletes; } -> {tip} The `SoftDeletes` trait will automatically cast the `deleted_at` attribute to a `DateTime` / `Carbon` instance for you. +> **Note** +> The `SoftDeletes` trait will automatically cast the `deleted_at` attribute to a `DateTime` / `Carbon` instance for you. You should also add the `deleted_at` column to your database table. The Laravel [schema builder](/docs/{{version}}/migrations) contains a helper method to create this column: @@ -972,7 +989,8 @@ You may test your `prunable` query by executing the `model:prune` command with t php artisan model:prune --pretend ``` -> {note} Soft deleting models will be permanently deleted (`forceDelete`) if they match the prunable query. +> **Warning** +> Soft deleting models will be permanently deleted (`forceDelete`) if they match the prunable query. #### Mass Pruning @@ -1074,7 +1092,8 @@ The `Scope` interface requires you to implement one method: `apply`. The `apply` } } -> {tip} If your global scope is adding columns to the select clause of the query, you should use the `addSelect` method instead of `select`. This will prevent the unintentional replacement of the query's existing select clause. +> **Note** +> If your global scope is adding columns to the select clause of the query, you should use the `addSelect` method instead of `select`. This will prevent the unintentional replacement of the query's existing select clause. #### Applying Global Scopes @@ -1264,7 +1283,8 @@ The `is` and `isNot` methods are also available when using the `belongsTo`, `has ## Events -> {tip} Want to broadcast your Eloquent events directly to your client-side application? Check out Laravel's [model event broadcasting](/docs/{{version}}/broadcasting#model-broadcasting). +> **Note** +> Want to broadcast your Eloquent events directly to your client-side application? Check out Laravel's [model event broadcasting](/docs/{{version}}/broadcasting#model-broadcasting). 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`, `trashed`, `forceDeleted`, `restoring`, `restored`, and `replicating`. @@ -1297,7 +1317,8 @@ To start listening to model events, define a `$dispatchesEvents` property on you After defining and mapping your Eloquent events, you may use [event listeners](/docs/{{version}}/events#defining-listeners) to handle the events. -> {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 mass updates or deletes. +> **Warning** +> 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 mass updates or deletes. ### Using Closures @@ -1440,7 +1461,8 @@ Alternatively, you may list your observers within an `$observers` property of yo User::class => [UserObserver::class], ]; -> {tip} There are additional events an observer can listen to, such as `saving` and `retrieved`. These events are described within the [events](#events) documentation. +> **Note** +> There are additional events an observer can listen to, such as `saving` and `retrieved`. These events are described within the [events](#events) documentation. #### Observers & Database Transactions @@ -1497,3 +1519,9 @@ Sometimes you may wish to "save" a given model without dispatching any events. Y $user->name = 'Victoria Faith'; $user->saveQuietly(); + +You may also "update", "delete", "soft delete", "restore", and "replicate" a given model without dispatching any events: + + $user->deleteQuietly(); + + $user->restoreQuietly(); diff --git a/envoy.md b/envoy.md index 88483b61e31..89c0af51c6f 100644 --- a/envoy.md +++ b/envoy.md @@ -323,7 +323,7 @@ Envoy also supports sending notifications to [Telegram](https://telegram.org) af ### Microsoft Teams -Envoy also supports sending notifications to [Microsoft Teams](https://www.microsoft.com/en-us/microsoft-teams) after each task is executed. The `@microsoftTeams` directive accepts a Teams Webhook (required), a message, theme color (success, info, warning, error), and an array of options. You may retrieve your Teams Webook by creating a new [incoming webhook](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook). The Teams API has many other attributes to customize your message box like title, summary, and sections. You can find more information on the [Microsoft Teams documentation](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL#example-of-connector-message). You should pass the entire Webhook URL into the `@microsoftTeams` directive: +Envoy also supports sending notifications to [Microsoft Teams](https://www.microsoft.com/en-us/microsoft-teams) after each task is executed. The `@microsoftTeams` directive accepts a Teams Webhook (required), a message, theme color (success, info, warning, error), and an array of options. You may retrieve your Teams Webhook by creating a new [incoming webhook](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook). The Teams API has many other attributes to customize your message box like title, summary, and sections. You can find more information on the [Microsoft Teams documentation](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using?tabs=cURL#example-of-connector-message). You should pass the entire Webhook URL into the `@microsoftTeams` directive: ```blade @finished diff --git a/errors.md b/errors.md index 513c6965dfb..60690789777 100644 --- a/errors.md +++ b/errors.md @@ -57,7 +57,8 @@ When you register a custom exception reporting callback using the `reportable` m return false; }); -> {tip} To customize the exception reporting for a given exception, you may also utilize [reportable exceptions](/docs/{{version}}/errors#renderable-exceptions). +> **Note** +> To customize the exception reporting for a given exception, you may also utilize [reportable exceptions](/docs/{{version}}/errors#renderable-exceptions). #### Global Log Context @@ -155,7 +156,8 @@ When building your application, there will be some types of exceptions you simpl 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. +> **Note** +> 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. ### Rendering Exceptions @@ -229,7 +231,7 @@ Instead of type-checking exceptions in the exception handler's `register` method */ public function render($request) { - return response(...); + return response(/* ... */); } } @@ -262,7 +264,8 @@ If your exception contains custom reporting logic that is only necessary when ce return false; } -> {tip} You may type-hint any required dependencies of the `report` method and they will automatically be injected into the method by Laravel's [service container](/docs/{{version}}/container). +> **Note** +> You may type-hint any required dependencies of the `report` method and they will automatically be injected into the method by Laravel's [service container](/docs/{{version}}/container). ## HTTP Exceptions diff --git a/events.md b/events.md index 4daf335edfa..10faa6c872a 100644 --- a/events.md +++ b/events.md @@ -42,7 +42,8 @@ The `App\Providers\EventServiceProvider` included with your Laravel application ], ]; -> {tip} The `event:list` command may be used to display a list of all events and listeners registered by your application. +> **Note** +> The `event:list` command may be used to display a list of all events and listeners registered by your application. ### Generating Events & Listeners @@ -264,7 +265,8 @@ Next, let's take a look at the listener for our example event. Event listeners r } } -> {tip} Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel [service container](/docs/{{version}}/container), so dependencies will be injected automatically. +> **Note** +> Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel [service container](/docs/{{version}}/container), so dependencies will be injected automatically. #### Stopping The Propagation Of An Event @@ -439,7 +441,8 @@ If your queue connection's `after_commit` configuration option is set to `false` public $afterCommit = true; } -> {tip} To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). +> **Note** +> To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). ### Handling Failed Jobs @@ -559,7 +562,8 @@ To dispatch an event, you may call the static `dispatch` method on the event. Th OrderShipped::dispatchUnless($condition, $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. +> **Note** +> 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. ## Event Subscribers diff --git a/facades.md b/facades.md index 2b4aec683fa..2a8e9faeb8e 100644 --- a/facades.md +++ b/facades.md @@ -304,3 +304,4 @@ Validator | [Illuminate\Validation\Factory](https://laravel.com/api/{{version} Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/api/{{version}}/Illuminate/Validation/Validator.html) |   View | [Illuminate\View\Factory](https://laravel.com/api/{{version}}/Illuminate/View/Factory.html) | `view` View (Instance) | [Illuminate\View\View](https://laravel.com/api/{{version}}/Illuminate/View/View.html) |   +Vite | [Illuminate\Foundation\Vite](https://laravel.com/api/{{version}}/Illuminate/Foundation/Vite.html) |   diff --git a/filesystem.md b/filesystem.md index 6a8f2ca9e5e..f1304477a2d 100644 --- a/filesystem.md +++ b/filesystem.md @@ -34,7 +34,8 @@ Laravel's filesystem configuration file is located at `config/filesystems.php`. 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. +> **Note** +> You may configure as many disks as you like and may even have multiple disks that use the same driver. ### The Local Driver @@ -146,7 +147,7 @@ Laravel's Flysystem integrations work great with SFTP; however, a sample configu By default, your application's `filesystems` configuration file contains a disk configuration for the `s3` disk. In addition to using this disk to interact with Amazon S3, you may use it to interact with any S3 compatible file storage service such as [MinIO](https://github.com/minio/minio) or [DigitalOcean Spaces](https://www.digitalocean.com/products/spaces/). -Typically, after updating the disk's credentials to match the credentials of the service you are planning to use, you only need to update the value of the `url` configuration option. This option's value is typically defined via the `AWS_ENDPOINT` environment variable: +Typically, after updating the disk's credentials to match the credentials of the service you are planning to use, you only need to update the value of the `endpoint` configuration option. This option's value is typically defined via the `AWS_ENDPOINT` environment variable: 'endpoint' => env('AWS_ENDPOINT', 'https://minio:9000'), @@ -218,7 +219,8 @@ You may use the `url` method to get the URL for a given file. If you are using t When using the `local` driver, all files that should be publicly accessible should be placed in the `storage/app/public` directory. Furthermore, you should [create a symbolic link](#the-public-disk) at `public/storage` which points to the `storage/app/public` directory. -> {note} When using the `local` driver, the return value of `url` is not URL encoded. For this reason, we recommend always storing your files using names that will create valid URLs. +> **Warning** +> When using the `local` driver, the return value of `url` is not URL encoded. For this reason, we recommend always storing your files using names that will create valid URLs. #### Temporary URLs @@ -420,7 +422,8 @@ You may also use the `putFileAs` method on the `Storage` facade, which will perf 'avatars', $request->file('avatar'), $request->user()->id ); -> {note} Unprintable and invalid unicode characters will automatically be removed from file paths. Therefore, you may wish to sanitize your file paths before passing them to Laravel's file storage methods. File paths are normalized using the `League\Flysystem\WhitespacePathNormalizer::normalizePath` method. +> **Warning** +> Unprintable and invalid unicode characters will automatically be removed from file paths. Therefore, you may wish to sanitize your file paths before passing them to Laravel's file storage methods. File paths are normalized using the `League\Flysystem\WhitespacePathNormalizer::normalizePath` method. #### Specifying A Disk diff --git a/fortify.md b/fortify.md index 8c17a250d5e..f811d79bc97 100644 --- a/fortify.md +++ b/fortify.md @@ -32,7 +32,8 @@ Since Fortify does not provide its own user interface, it is meant to be paired with your own user interface which makes requests to the routes it registers. We will discuss exactly how to make requests to these routes in the remainder of this documentation. -> {tip} Remember, Fortify is a package that is meant to give you a head start implementing Laravel's authentication features. **You are not required to use it.** You are always free to manually interact with Laravel's authentication services by following the documentation available in the [authentication](/docs/{{version}}/authentication), [password reset](/docs/{{version}}/passwords), and [email verification](/docs/{{version}}/verification) documentation. +> **Note** +> Remember, Fortify is a package that is meant to give you a head start implementing Laravel's authentication features. **You are not required to use it.** You are always free to manually interact with Laravel's authentication services by following the documentation available in the [authentication](/docs/{{version}}/authentication), [password reset](/docs/{{version}}/passwords), and [email verification](/docs/{{version}}/verification) documentation. ### What Is Fortify? @@ -362,7 +363,7 @@ To disable two factor authentication, your application should make a DELETE requ To begin implementing our application's registration functionality, we need to instruct Fortify how to return our "register" view. Remember, Fortify is a headless authentication library. If you would like a frontend implementation of Laravel's authentication features that are already completed for you, you should use an [application starter kit](/docs/{{version}}/starter-kits). -All of the Fortify's view rendering logic may be customized using the appropriate methods available via the `Laravel\Fortify\Fortify` class. Typically, you should call this method from the `boot` method of your `App\Providers\FortifyServiceProvider` class: +All of Fortify's view rendering logic may be customized using the appropriate methods available via the `Laravel\Fortify\Fortify` class. Typically, you should call this method from the `boot` method of your `App\Providers\FortifyServiceProvider` class: ```php use Laravel\Fortify\Fortify; @@ -386,7 +387,7 @@ Fortify will take care of defining the `/register` route that returns this view. The `/register` endpoint expects a string `name`, string email address / username, `password`, and `password_confirmation` fields. The name of the email / username field should match the `username` configuration value defined within your application's `fortify` configuration file. -If the registration attempt is successful, Fortify will redirect the user to the URI configured via the `home` configuration option within your application's `fortify` configuration file. If the login request was an XHR request, a 200 HTTP response will be returned. +If the registration attempt is successful, Fortify will redirect the user to the URI configured via the `home` configuration option within your application's `fortify` configuration file. If the request was an XHR request, a 201 HTTP response will be returned. If the request was not successful, the user will be redirected back to the registration screen and the validation errors will be available to you via the shared `$errors` [Blade template variable](/docs/{{version}}/validation#quick-displaying-the-validation-errors). Or, in the case of an XHR request, the validation errors will be returned with a 422 HTTP response. diff --git a/frontend.md b/frontend.md new file mode 100644 index 00000000000..50174564a90 --- /dev/null +++ b/frontend.md @@ -0,0 +1,197 @@ +# Frontend + +- [Introduction](#introduction) +- [Using PHP](#using-php) + - [PHP & Blade](#php-and-blade) + - [Livewire](#livewire) + - [Starter Kits](#php-starter-kits) +- [Using Vue / React](#using-vue-react) + - [Inertia](#inertia) + - [Starter Kits](#inertia-starter-kits) +- [Bundling Assets](#bundling-assets) + + +## Introduction + +Laravel is a backend framework that provides all of the features you need to build modern web applications, such as [routing](/docs/{{version}}/routing), [validation](/docs/{{version}}/validation), [caching](/docs/{{version}}/cache), [queues](/docs/{{version}}/queues), [file storage](/docs/{{version}}/filesystem), and more. However, we believe it's important to offer developers a beautiful full-stack experience, including powerful approaches for building your application's frontend. + +There are two primary ways to tackle frontend development when building an application with Laravel, and which approach you choose is determined by whether you would like to build your frontend by leveraging PHP or by using JavaScript frameworks such as Vue and React. We'll discuss both of these options below so that you can make an informed decision regarding the best approach to frontend development for your application. + + +## Using PHP + + +### PHP & Blade + +In the past, most PHP applications rendered HTML to the browser using simple HTML templates interspersed with PHP `echo` statements which render data that was retrieved from a database during the request: + +```blade +
+ + Hello, name; ?>
+ +
+``` + +In Laravel, this approach to rendering HTML can still be achieved using [views](/docs/{{version}}/views) and [Blade](/docs/{{version}}/blade). Blade is an extremely light-weight templating language that provides convenient, short syntax for displaying data, iterating over data, and more: + +```blade +
+ @foreach ($users as $user) + Hello, {{ $user->name }}
+ @endforeach +
+``` + +When building applications in this fashion, form submissions and other page interactions typically receive an entirely new HTML document from the server and the entire page is re-rendered by the browser. Even today, many applications may be perfectly suited to having their frontends constructed in this way using simple Blade templates. + + +#### Growing Expectations + +However, as user expectations regarding web applications have matured, many developers have found the need to build more dynamic frontends with interactions that feel more polished. In light of this, some developers choose to begin building their application's frontend using JavaScript frameworks such as Vue and React. + +Others, preferring to stick with the backend language they are comfortable with, have developed solutions that allow the construction of modern web application UIs while still primarily utilizing their backend language of choice. For example, in the [Rails](https://rubyonrails.org/) ecosystem, this has spurred the creation of libraries such as [Turbo](https://turbo.hotwired.dev/) [Hotwire](https://hotwired.dev/), and [Stimulus](https://stimulus.hotwired.dev/). + +Within the Laravel ecosystem, the need to create modern, dynamic frontends by primarily using PHP has led to the creation of [Laravel Livewire](https://laravel-livewire.com) and [Alpine.js](https://alpinejs.dev/). + + +### Livewire + +[Laravel Livewire](https://laravel-livewire.com) is a framework for building Laravel powered frontends that feel dynamic, modern, and alive just like frontends built with modern JavaScript frameworks like Vue and React. + +When using Livewire, you will create Livewire "components" that render a discrete portion of your UI and expose methods and data that can be invoked and interacted with from your application's frontend. For example, a simple "Counter" component might look like the following: + +```php +count++; + } + + public function render() + { + return view('livewire.counter'); + } +} +``` + +And, the corresponding template for the counter would be written like so: + +```blade +
+ +

{{ $count }}

+
+``` + +As you can see, Livewire enables you to write new HTML attributes such as `wire:click` that connect your Laravel application's frontend and backend. In addition, you can render your component's current state using simple Blade expressions. + +For many, Livewire has revolutionized frontend development with Laravel, allowing them to stay within the comfort of Laravel while constructing modern, dynamic web applications. Typically, developers using Livewire will also utilize [Alpine.js](https://alpinejs.dev/) to "sprinkle" JavaScript onto their frontend only where it is needed, such as in order to render a dialog window. + +If you're new to Laravel, we recommend getting familiar with the basic usage of [views](/docs/{{version}}/views) and [Blade](/docs/{{version}}/blade). Then, consult the official [Laravel Livewire documentation](https://laravel-livewire.com/docs) to learn how to take your application to the next level with interactive Livewire components. + + +### Starter Kits + +If you would like to build your frontend using PHP and Livewire, you can leverage our Breeze or Jetstream [starter kits](/docs/{{version}}/starter-kits) to jump-start your application's development. Both of these starter kits scaffold your application's backend and frontend authentication flow using [Blade](/docs/{{version}}/blade) and [Tailwind](https://tailwindcss.com) so that you can simply start building your next big idea. + + +## Using Vue / React + +Although it's possible to build modern frontends using Laravel and Livewire, many developers still prefer to leverage the power of a JavaScript framework like Vue or React. This allows developers to take advantage of the rich ecosystem of JavaScript packages and tools available via NPM. + +However, without additional tooling, pairing Laravel with Vue or React would leave us needing to solve a variety of complicated problems such as client-side routing, data hydration, and authentication. Client-side routing is often simplified by using opinionated Vue / React frameworks such as [Nuxt](https://nuxtjs.org/) and [Next](https://nextjs.org/); however, data hydration and authentication remain complicated and cumbersome problems to solve when pairing a backend framework like Laravel with these frontend frameworks. + +In addition, developers are left maintaining two separate code repositories, often needing to coordinate maintenance, releases, and deployments across both repositories. While these problems are not insurmountable, we don't believe it's a productive or enjoyable way to develop applications. + + +### Inertia + +Thankfully, Laravel offers the best of both worlds. [Inertia](https://inertiajs.com) bridges the gap between your Laravel application and your modern Vue or React frontend, allowing you to build full-fledged, modern frontends using Vue or React while leveraging Laravel routes and controllers for routing, data hydration, and authentication ā€” all within a single code repository. With this approach, you can enjoy the full power of both Laravel and Vue / React without crippling the capabilities of either tool. + +After installing Inertia into your Laravel application, you will write routes and controllers like normal. However, instead of returning a Blade template from your controller, you will return an Inertia page: + +```php + User::findOrFail($id) + ]); + } +} +``` + +An Inertia page corresponds to a Vue or React component, typically stored within the `resources/js/Pages` directory of your application. The data given to the page via the `Inertia::render` method will be used to hydrate the "props" of the page component: + +```vue + + + +``` + +As you can see, Inertia allows you to leverage the full power of Vue or React when building your frontend, while providing a light-weight bridge between your Laravel powered backend and your JavaScript powered frontend. + +#### Server-Side Rendering + +If you're concerned about diving into Inertia because your application requires server-side rendering, don't worry. Inertia offers [server-side rendering support](https://inertiajs.com/server-side-rendering). And, when deploying your application via [Laravel Forge](https://forge.laravel.com), it's a breeze to ensure that Inertia's server-side rendering process is always running. + + +### Starter Kits + +If you would like to build your frontend using Inertia and Vue / React, you can leverage our Breeze or Jetstream [starter kits](/docs/{{version}}/starter-kits#breeze-and-inertia) to jump-start your application's development. Both of these starter kits scaffold your application's backend and frontend authentication flow using Inertia, Vue / React, [Tailwind](https://tailwindcss.com), and [Vite](https://vitejs.dev) so that you can start building your next big idea. + + +## Bundling Assets + +Regardless of whether you choose to develop your frontend using Blade and Livewire or Vue / React and Inertia, you will likely need to bundle your application's CSS into production ready assets. Of course, if you choose to build your application's frontend with Vue or React, you will also need to bundle your components into browser ready JavaScript assets. + +By default, Laravel utilizes [Vite](https://vitejs.dev) to bundle your assets. Vite provides lightning-fast build times and near instantaneous Hot Module Replacement (HMR) during local development. In all new Laravel applications, including those using our [starter kits](/docs/{{version}}/starter-kits), you will find a `vite.config.js` file that loads our light-weight Laravel Vite plugin that makes Vite a joy to use with Laravel applications. + +The fastest way to get started with Laravel and Vite is by beginning your application's development using [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze), our simplest starter kit that jump-starts your application by providing frontend and backend authentication scaffolding. + +> **Note** +> For more detailed documentation on utilizing Vite with Laravel, please see our [dedicated documentation on bundling and compiling your assets](/docs/{{version}}/vite). diff --git a/hashing.md b/hashing.md index 414362496d8..d3f278d254f 100644 --- a/hashing.md +++ b/hashing.md @@ -73,7 +73,8 @@ If you are using the Argon2 algorithm, the `make` method allows you to manage th 'threads' => 2, ]); -> {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). +> **Note** +> 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). ### Verifying That A Password Matches A Hash diff --git a/helpers.md b/helpers.md index 1ff62403275..d74176e2053 100644 --- a/helpers.md +++ b/helpers.md @@ -45,11 +45,14 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Arr::hasAny](#method-array-hasany) [Arr::isAssoc](#method-array-isassoc) [Arr::isList](#method-array-islist) +[Arr::join](#method-array-join) [Arr::keyBy](#method-array-keyby) [Arr::last](#method-array-last) +[Arr::map](#method-array-map) [Arr::only](#method-array-only) [Arr::pluck](#method-array-pluck) [Arr::prepend](#method-array-prepend) +[Arr::prependKeysWith](#method-array-prependkeyswith) [Arr::pull](#method-array-pull) [Arr::query](#method-array-query) [Arr::random](#method-array-random) @@ -109,8 +112,10 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [Str::excerpt](#method-excerpt) [Str::finish](#method-str-finish) [Str::headline](#method-str-headline) +[Str::inlineMarkdown](#method-str-inline-markdown) [Str::is](#method-str-is) [Str::isAscii](#method-str-is-ascii) +[Str::isJson](#method-str-is-json) [Str::isUuid](#method-str-is-uuid) [Str::kebab](#method-kebab-case) [Str::lcfirst](#method-str-lcfirst) @@ -172,6 +177,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [between](#method-fluent-str-between) [betweenFirst](#method-fluent-str-between-first) [camel](#method-fluent-str-camel) +[classBasename](#method-fluent-str-class-basename) [contains](#method-fluent-str-contains) [containsAll](#method-fluent-str-contains-all) [dirname](#method-fluent-str-dirname) @@ -180,10 +186,12 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [exactly](#method-fluent-str-exactly) [explode](#method-fluent-str-explode) [finish](#method-fluent-str-finish) +[inlineMarkdown](#method-fluent-str-inline-markdown) [is](#method-fluent-str-is) [isAscii](#method-fluent-str-is-ascii) [isEmpty](#method-fluent-str-is-empty) [isNotEmpty](#method-fluent-str-is-not-empty) +[isJson](#method-fluent-str-is-json) [isUuid](#method-fluent-str-is-uuid) [kebab](#method-fluent-str-kebab) [lcfirst](#method-fluent-str-lcfirst) @@ -236,6 +244,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [whenStartsWith](#method-fluent-str-when-starts-with) [whenEndsWith](#method-fluent-str-when-ends-with) [whenExactly](#method-fluent-str-when-exactly) +[whenNotExactly](#method-fluent-str-when-not-exactly) [whenIs](#method-fluent-str-when-is) [whenIsAscii](#method-fluent-str-when-is-ascii) [whenIsUuid](#method-fluent-str-when-is-uuid) @@ -288,6 +297,7 @@ Laravel includes a variety of global "helper" PHP functions. Many of these funct [encrypt](#method-encrypt) [env](#method-env) [event](#method-event) +[fake](#method-fake) [filled](#method-filled) [info](#method-info) [logger](#method-logger) @@ -601,14 +611,31 @@ The `Arr::isList` method returns `true` if the given array's keys are sequential use Illuminate\Support\Arr; - $isAssoc = Arr::isList(['foo', 'bar', 'baz']); + $isList = Arr::isList(['foo', 'bar', 'baz']); // true - $isAssoc = Arr::isList(['product' => ['name' => 'Desk', 'price' => 100]]); + $isList = Arr::isList(['product' => ['name' => 'Desk', 'price' => 100]]); // false + +#### `Arr::join()` {.collection-method} + +The `Arr::join` method joins array elements with a string. Using this method's second argument, you may also specify the joining string for the final element of the array: + + use Illuminate\Support\Arr; + + $array = ['Tailwind', 'Alpine', 'Laravel', 'Livewire']; + + $joined = Arr::join($array, ', '); + + // Tailwind, Alpine, Laravel, Livewire + + $joined = Arr::join($array, ', ', ' and '); + + // Tailwind, Alpine, Laravel and Livewire + #### `Arr::keyBy()` {.collection-method} @@ -651,6 +678,21 @@ A default value may be passed as the third argument to the method. This value wi $last = Arr::last($array, $callback, $default); + +#### `Arr::map()` {.collection-method} + +The `Arr::map` method iterates through the array and passes each value and key to the given callback. The array value is replaced by the value returned by the callback: + + use Illuminate\Support\Arr; + + $array = ['first' => 'james', 'last' => 'kirk']; + + $mapped = Arr::map($array, function ($value, $key) { + return ucfirst($value); + }); + + // ['first' => 'James', 'last' => 'Kirk'] + #### `Arr::only()` {.collection-method} @@ -711,6 +753,27 @@ If needed, you may specify the key that should be used for the value: // ['name' => 'Desk', 'price' => 100] + +#### `Arr::prependKeysWith()` {.collection-method} + +The `Arr::prependKeysWith` prepends all key names of an associative array with the given prefix: + + use Illuminate\Support\Arr; + + $array = [ + 'name' => 'Desk', + 'price' => 100, + ]; + + $keyed = Arr::prependKeysWith($array, 'product.'); + + /* + [ + 'product.name' => 'Desk', + 'product.price' => 100, + ] + */ + #### `Arr::pull()` {.collection-method} @@ -1386,6 +1449,17 @@ The `Str::headline` method will convert strings delimited by casing, hyphens, or // Email Notification Sent + +#### `Str::inlineMarkdown()` {.collection-method} + +The `Str::inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: + + use Illuminate\Support\Str; + + $html = Str::inlineMarkdown('**Laravel**'); + + // Laravel + #### `Str::is()` {.collection-method} @@ -1416,6 +1490,25 @@ The `Str::isAscii` method determines if a given string is 7 bit ASCII: // false + +#### `Str::isJson()` {.collection-method} + +The `Str::isJson` method determines if the given string is valid JSON: + + use Illuminate\Support\Str; + + $result = Str::isJson('[1,2,3]'); + + // true + + $result = Str::isJson('{"first": "John", "last": "Doe"}'); + + // true + + $result = Str::isJson('{first: "John", last: "Doe"}'); + + // false + #### `Str::isUuid()` {.collection-method} @@ -2104,6 +2197,17 @@ The `camel` method converts the given string to `camelCase`: // fooBar + +#### `classBasename` {.collection-method} + +The `classBasename` method returns the class name of the given class with the class's namespace removed: + + use Illuminate\Support\Str; + + $class = Str::of('Foo\Bar\Baz')->classBasename(); + + // Baz + #### `contains` {.collection-method} @@ -2239,6 +2343,17 @@ The `finish` method adds a single instance of the given value to a string if it // this/string/ + +#### `inlineMarkdown` {.collection-method} + +The `inlineMarkdown` method converts GitHub flavored Markdown into inline HTML using [CommonMark](https://commonmark.thephpleague.com/). However, unlike the `markdown` method, it does not wrap all generated HTML in a block-level element: + + use Illuminate\Support\Str; + + $html = Str::of('**Laravel**')->inlineMarkdown(); + + // Laravel + #### `is` {.collection-method} @@ -2300,6 +2415,25 @@ The `isNotEmpty` method determines if the given string is not empty: // true + +#### `isJson` {.collection-method} + +The `isJson` method determines if a given string is valid JSON: + + use Illuminate\Support\Str; + + $result = Str::of('[1,2,3]')->isJson(); + + // true + + $result = Str::of('{"first": "John", "last": "Doe"}')->isJson(); + + // true + + $result = Str::of('{first: "John", last: "Doe"}')->isJson(); + + // false + #### `isUuid` {.collection-method} @@ -2833,7 +2967,7 @@ The `tap` method passes the string to the given closure, allowing you to examine $string = Str::of('Laravel') ->append(' Framework') ->tap(function ($string) { - dump('String after append: ' . $string); + dump('String after append: '.$string); }) ->upper(); @@ -3033,6 +3167,19 @@ The `whenExactly` method invokes the given closure if the string exactly matches // 'Laravel' + +#### `whenNotExactly` {.collection-method} + +The `whenNotExactly` method invokes the given closure if the string does not exactly match the given string. The closure will receive the fluent string instance: + + use Illuminate\Support\Str; + + $string = Str::of('framework')->whenNotExactly('laravel', function ($string) { + return $string->title(); + }); + + // 'Framework' + #### `whenIs` {.collection-method} @@ -3409,7 +3556,8 @@ The `env` function retrieves the value of an [environment variable](/docs/{{vers $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`. +> **Warning** +> 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`. #### `event()` {.collection-method} @@ -3418,6 +3566,27 @@ The `event` function dispatches the given [event](/docs/{{version}}/events) to i event(new UserRegistered($user)); + +#### `fake()` {.collection-method} + +The `fake` function resolves a [Faker](https://github.com/FakerPHP/Faker) singleton from the container, which can be useful when creating fake data in model factories, database seeding, tests, and prototyping views: + +```blade +@for($i = 0; $i < 10; $i++) +
+
Name
+
{{ fake()->name() }}
+ +
Email
+
{{ fake()->unique()->safeEmail() }}
+
+@endfor +``` + +By default, the `fake` function will utilize the `app.faker_locale` configuration option in your `config/app.php` configuration file; however, you may also specify the locale by passing it to the `fake` function. Each locale will resolve an individual singleton: + + fake('nl_NL')->name() + #### `filled()` {.collection-method} @@ -3600,13 +3769,13 @@ If you would like to manually calculate the number of milliseconds to sleep betw return retry(5, function () { // ... - }, function ($attempt) { + }, function ($attempt, $exception) { return $attempt * 100; }); For convenience, you may provide an array as the first argument to the `retry` function. This array will be used to determine how many milliseconds to sleep between subsequent attempts: - return retry([100, 200] function () { + return retry([100, 200], function () { // Sleep for 100ms on first retry, 200ms on second retry... }); diff --git a/homestead.md b/homestead.md index b9947fa4a38..5c21eebdf0c 100644 --- a/homestead.md +++ b/homestead.md @@ -42,7 +42,8 @@ Laravel strives to make the entire PHP development experience delightful, includ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, MySQL, PostgreSQL, Redis, Memcached, Node, and all of the other software you need to develop amazing Laravel applications. -> {note} If you are using Windows, you may need to enable hardware virtualization (VT-x). It can usually be enabled via your BIOS. If you are using Hyper-V on a UEFI system you may additionally need to disable Hyper-V in order to access VT-x. +> **Warning** +> If you are using Windows, you may need to enable hardware virtualization (VT-x). It can usually be enabled via your BIOS. If you are using Hyper-V on a UEFI system you may additionally need to disable Hyper-V in order to access VT-x. ### Included Software @@ -73,6 +74,7 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - Sqlite3 - PostgreSQL 13 - Composer +- Docker - Node (With Yarn, Bower, Grunt, and Gulp) - Redis - Memcached @@ -105,7 +107,6 @@ Homestead runs on any Windows, macOS, or Linux system and includes Nginx, PHP, M - Chronograf - CouchDB - Crystal & Lucky Framework -- Docker - Elasticsearch - EventStoreDB - Gearman @@ -183,7 +184,8 @@ The `provider` key in your `Homestead.yaml` file indicates which Vagrant provide provider: virtualbox -> {note} If you are using Apple Silicon, you should add `box: laravel/homestead-arm` to your `Homestead.yaml` file. Apple Silicon requires the Parallels provider. +> **Warning** +> If you are using Apple Silicon, you should add `box: laravel/homestead-arm` to your `Homestead.yaml` file. Apple Silicon requires the Parallels provider. #### Configuring Shared Folders @@ -196,7 +198,8 @@ folders: to: /home/vagrant/project1 ``` -> {note} Windows users should not use the `~/` path syntax and instead should use the full path to their project, such as `C:\Users\user\Code\project1`. +> **Warning** +> Windows users should not use the `~/` path syntax and instead should use the full path to their project, such as `C:\Users\user\Code\project1`. You should always map individual applications to their own folder mapping instead of mapping a single large directory that contains all of your applications. When you map a folder, the virtual machine must keep track of all disk IO for *every* file in the folder. You may experience reduced performance if you have a large number of files in a folder: @@ -208,7 +211,8 @@ folders: to: /home/vagrant/project2 ``` -> {note} You should never mount `.` (the current directory) when using Homestead. This causes Vagrant to not map the current folder to `/vagrant` and will break optional features and cause unexpected results while provisioning. +> **Warning** +> You should never mount `.` (the current directory) when using Homestead. This causes Vagrant to not map the current folder to `/vagrant` and will break optional features and cause unexpected results while provisioning. To enable [NFS](https://www.vagrantup.com/docs/synced-folders/nfs.html), you may add a `type` option to your folder mapping: @@ -219,7 +223,8 @@ folders: type: "nfs" ``` -> {note} When using NFS on Windows, you should consider installing the [vagrant-winnfsd](https://github.com/winnfsd/vagrant-winnfsd) plug-in. This plug-in will maintain the correct user / group permissions for files and directories within the Homestead virtual machine. +> **Warning** +> When using NFS on Windows, you should consider installing the [vagrant-winnfsd](https://github.com/winnfsd/vagrant-winnfsd) plug-in. This plug-in will maintain the correct user / group permissions for files and directories within the Homestead virtual machine. You may also pass any options supported by Vagrant's [Synced Folders](https://www.vagrantup.com/docs/synced-folders/basic_usage.html) by listing them under the `options` key: @@ -246,7 +251,8 @@ sites: If you change the `sites` property after provisioning the Homestead virtual machine, you should execute the `vagrant reload --provision` command in your terminal to update the Nginx configuration on the virtual machine. -> {note} Homestead scripts are built to be as idempotent as possible. However, if you are experiencing issues while provisioning you should destroy and rebuild the machine by executing the `vagrant destroy && vagrant up` command. +> **Warning** +> Homestead scripts are built to be as idempotent as possible. However, if you are experiencing issues while provisioning you should destroy and rebuild the machine by executing the `vagrant destroy && vagrant up` command. #### Hostname Resolution @@ -324,7 +330,6 @@ features: - chronograf: true - couchdb: true - crystal: true - - docker: true - elasticsearch: version: 7.9.0 - eventstore: true @@ -356,7 +361,8 @@ features: You may specify a supported version of Elasticsearch, which must be an exact version number (major.minor.patch). The default installation will create a cluster named 'homestead'. You should never give Elasticsearch more than half of the operating system's memory, so make sure your Homestead virtual machine has at least twice the Elasticsearch allocation. -> {tip} Check out the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current) to learn how to customize your configuration. +> **Note** +> Check out the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current) to learn how to customize your configuration. #### MariaDB @@ -453,7 +459,8 @@ sites: to: /home/vagrant/project2/public ``` -> {note} You should ensure that you have configured a [folder mapping](#configuring-shared-folders) for the project's directory before adding the site. +> **Warning** +> You should ensure that you have configured a [folder mapping](#configuring-shared-folders) for the project's directory before adding the site. If Vagrant is not automatically managing your "hosts" file, you may need to add the new site to that file as well. On macOS and Linux, this file is located at `/etc/hosts`. On Windows, it is located at `C:\Windows\System32\drivers\etc\hosts`: @@ -588,7 +595,8 @@ php81 A `homestead` database is configured for both MySQL and PostgreSQL out of the box. To connect to your MySQL or PostgreSQL database from your host machine's database client, you should connect to `127.0.0.1` on port `33060` (MySQL) or `54320` (PostgreSQL). The username and password for both databases is `homestead` / `secret`. -> {note} You should only use these non-standard ports when connecting to the databases from your host machine. You will use the default 3306 and 5432 ports in your Laravel application's `database` configuration file since Laravel is running _within_ the virtual machine. +> **Warning** +> You should only use these non-standard ports when connecting to the databases from your host machine. You will use the default 3306 and 5432 ports in your Laravel application's `database` configuration file since Laravel is running _within_ the virtual machine. ### Database Backups @@ -702,7 +710,8 @@ After running the command, you will see an Ngrok screen appear which contains th share homestead.test -region=eu -subdomain=laravel ``` -> {note} Remember, Vagrant is inherently insecure and you are exposing your virtual machine to the Internet when running the `share` command. +> **Warning** +> Remember, Vagrant is inherently insecure and you are exposing your virtual machine to the Internet when running the `share` command. ## Debugging & Profiling @@ -714,7 +723,8 @@ Homestead includes support for step debugging using [Xdebug](https://xdebug.org) By default, Xdebug is already running and ready to accept connections. If you need to enable Xdebug on the CLI, execute the `sudo phpenmod xdebug` command within your Homestead virtual machine. Next, follow your IDE's instructions to enable debugging. Finally, configure your browser to trigger Xdebug with an extension or [bookmarklet](https://www.jetbrains.com/phpstorm/marklets/). -> {note} Xdebug causes PHP to run significantly slower. To disable Xdebug, run `sudo phpdismod xdebug` within your Homestead virtual machine and restart the FPM service. +> **Warning** +> Xdebug causes PHP to run significantly slower. To disable Xdebug, run `sudo phpdismod xdebug` within your Homestead virtual machine and restart the FPM service. #### Autostarting Xdebug @@ -723,8 +733,9 @@ When debugging functional tests that make requests to the web server, it is easi ```ini ; If Homestead.yaml contains a different subnet for the IP address, this address may be different... -xdebug.remote_host = 192.168.10.1 -xdebug.remote_autostart = 1 +xdebug.client_host = 192.168.10.1 +xdebug.mode = debug +xdebug.start_with_request = yes ``` diff --git a/horizon.md b/horizon.md index 359fabb6902..b4a59566530 100644 --- a/horizon.md +++ b/horizon.md @@ -17,7 +17,8 @@ ## Introduction -> {tip} Before digging into Laravel Horizon, you should familiarize yourself with Laravel's base [queue services](/docs/{{version}}/queues). Horizon augments Laravel's queue with additional features that may be confusing if you are not already familiar with the basic queue features offered by Laravel. +> **Note** +> Before digging into Laravel Horizon, you should familiarize yourself with Laravel's base [queue services](/docs/{{version}}/queues). Horizon augments Laravel's queue with additional features that may be confusing if you are not already familiar with the basic queue features offered by Laravel. [Laravel Horizon](https://github.com/laravel/horizon) provides a beautiful dashboard and code-driven configuration for your Laravel powered [Redis queues](/docs/{{version}}/queues). Horizon allows you to easily monitor key metrics of your queue system such as job throughput, runtime, and job failures. @@ -28,7 +29,8 @@ When using Horizon, all of your queue worker configuration is stored in a single ## Installation -> {note} Laravel Horizon requires that you use [Redis](https://redis.io) to power your queue. Therefore, you should ensure that your queue connection is set to `redis` in your application's `config/queue.php` configuration file. +> **Warning** +> Laravel Horizon requires that you use [Redis](https://redis.io) to power your queue. Therefore, you should ensure that your queue connection is set to `redis` in your application's `config/queue.php` configuration file. You may install Horizon into your project using the Composer package manager: @@ -47,7 +49,8 @@ php artisan horizon:install After publishing Horizon's assets, its primary configuration file will be located at `config/horizon.php`. This configuration file allows you to configure the queue worker options for your application. Each configuration option includes a description of its purpose, so be sure to thoroughly explore this file. -> {note} Horizon uses a Redis connection named `horizon` internally. This Redis connection name is reserved and should not be assigned to another Redis connection in the `database.php` configuration file or as the value of the `use` option in the `horizon.php` configuration file. +> **Warning** +> Horizon uses a Redis connection named `horizon` internally. This Redis connection name is reserved and should not be assigned to another Redis connection in the `database.php` configuration file or as the value of the `use` option in the `horizon.php` configuration file. #### Environments @@ -72,7 +75,8 @@ After installation, the primary Horizon configuration option that you should fam When you start Horizon, it will use the worker process configuration options for the environment that your application is running on. Typically, the environment is determined by the value of the `APP_ENV` [environment variable](/docs/{{version}}/configuration#determining-the-current-environment). For example, the default `local` Horizon environment is configured to start three worker processes and automatically balance the number of worker processes assigned to each queue. The default `production` environment is configured to start a maximum of 10 worker processes and automatically balance the number of worker processes assigned to each queue. -> {note} You should ensure that the `environments` portion of your `horizon` configuration file contains an entry for each [environment](/docs/{{version}}/configuration#environment-configuration) on which you plan to run Horizon. +> **Warning** +> You should ensure that the `environments` portion of your `horizon` configuration file contains an entry for each [environment](/docs/{{version}}/configuration#environment-configuration) on which you plan to run Horizon. #### Supervisors @@ -112,9 +116,9 @@ When using the `auto` strategy, you may define the `minProcesses` and `maxProces ], ], -The `balanceMaxShift` and `balanceCooldown` configuration values to determine how quickly Horizon will scale to meet worker demand. In the example above, a maximum of one new process will be created or destroyed every three seconds. You are free to tweak these values as necessary based on your application's needs. +The `balanceMaxShift` and `balanceCooldown` configuration values determine how quickly Horizon will scale to meet worker demand. In the example above, a maximum of one new process will be created or destroyed every three seconds. You are free to tweak these values as necessary based on your application's needs. -When the `balance` option is set to `false`, the default Laravel behavior will be used, which processes queues in the order they are listed in your configuration. +When the `balance` option is set to `false`, the default Laravel behavior will be used, wherein queues are processed in the order they are listed in your configuration. ### Dashboard Authorization @@ -220,7 +224,8 @@ Supervisor is a process monitor for the Linux operating system and will automati 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. +> **Note** +> 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. #### Supervisor Configuration @@ -241,7 +246,8 @@ stopwaitsecs=3600 When defining your Supervisor configuration, 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. -> {note} While the examples above are valid for Ubuntu based servers, the location and file extension expected of Supervisor configuration files may vary between other server operating systems. Please consult your server's documentation for more information. +> **Warning** +> While the examples above are valid for Ubuntu based servers, the location and file extension expected of Supervisor configuration files may vary between other server operating systems. Please consult your server's documentation for more information. #### Starting Supervisor @@ -256,7 +262,8 @@ sudo supervisorctl update sudo supervisorctl start horizon ``` -> {tip} For more information on running Supervisor, consult the [Supervisor documentation](http://supervisord.org/index.html). +> **Note** +> For more information on running Supervisor, consult the [Supervisor documentation](http://supervisord.org/index.html). ## Tags @@ -337,7 +344,8 @@ If you would like to manually define the tags for one of your queueable objects, ## Notifications -> {note} When configuring Horizon to send Slack or SMS notifications, you should review the [prerequisites for the relevant notification channel](/docs/{{version}}/notifications). +> **Warning** +> When configuring Horizon to send Slack or SMS notifications, you should review the [prerequisites for the relevant notification channel](/docs/{{version}}/notifications). If you would like to be notified when one of your queues has a long wait time, you may use the `Horizon::routeMailNotificationsTo`, `Horizon::routeSlackNotificationsTo`, and `Horizon::routeSmsNotificationsTo` methods. You may call these methods from the `boot` method of your application's `App\Providers\HorizonServiceProvider`: diff --git a/http-client.md b/http-client.md index 41bfd85bd8b..805e30d2b8e 100644 --- a/http-client.md +++ b/http-client.md @@ -8,6 +8,7 @@ - [Timeout](#timeout) - [Retries](#retries) - [Error Handling](#error-handling) + - [Guzzle Middleware](#guzzle-middleware) - [Guzzle Options](#guzzle-options) - [Concurrent Requests](#concurrent-requests) - [Macros](#macros) @@ -148,45 +149,45 @@ For convenience, you may use the `acceptJson` method to quickly specify that you You may specify basic and digest authentication credentials using the `withBasicAuth` and `withDigestAuth` methods, respectively: // Basic authentication... - $response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(...); + $response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(/* ... */); // Digest authentication... - $response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(...); + $response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(/* ... */); #### Bearer Tokens 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(...); + $response = Http::withToken('token')->post(/* ... */); ### Timeout The `timeout` method may be used to specify the maximum number of seconds to wait for a response: - $response = Http::timeout(3)->get(...); + $response = Http::timeout(3)->get(/* ... */); If the given timeout is exceeded, an instance of `Illuminate\Http\Client\ConnectionException` will be thrown. You may specify the maximum number of seconds to wait while trying to connect to a server using the `connectTimeout` method: - $response = Http::connectTimeout(3)->get(...); + $response = Http::connectTimeout(3)->get(/* ... */); ### 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 the maximum number of times the request should be attempted and the number of milliseconds that Laravel should wait in between attempts: +If you would like the HTTP client to automatically retry the request if a client or server error occurs, you may use the `retry` method. The `retry` method accepts 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(...); + $response = Http::retry(3, 100)->post(/* ... */); If needed, you may pass a third argument to the `retry` method. The third argument should be a callable that determines if the retries should actually be attempted. For example, you may wish to only retry the request if the initial request encounters an `ConnectionException`: $response = Http::retry(3, 100, function ($exception, $request) { return $exception instanceof ConnectionException; - })->post(...); + })->post(/* ... */); -If a request attempt fails, you may wish to make a change to the request before a new attempt is made. You can achieve this by modifying request argument provided to the callable you provided to the `retry` method. For example, you might want to retry the request with a new authorization token if the first attempt returned an authentication error: +If a request attempt fails, you may wish to make a change to the request before a new attempt is made. You can achieve this by modifying the request argument provided to the callable you provided to the `retry` method. For example, you might want to retry the request with a new authorization token if the first attempt returned an authentication error: $response = Http::withToken($this->getToken())->retry(2, 0, function ($exception, $request) { if (! $exception instanceof RequestException || $exception->response->status() !== 401) { @@ -196,13 +197,14 @@ If a request attempt fails, you may wish to make a change to the request before $request->withToken($this->getNewToken()); return true; - })->post(...); + })->post(/* ... */); If all of the requests fail, an instance of `Illuminate\Http\Client\RequestException` will be thrown. If you would like to disable this behavior, you may provide a `throw` argument with a value of `false`. When disabled, the last response received by the client will be returned after all retries have been attempted: - $response = Http::retry(3, 100, throw: false)->post(...); + $response = Http::retry(3, 100, throw: false)->post(/* ... */); -> {note} If all of the requests fail because of a connection issue, a `Illuminate\Http\Client\ConnectionException` will still be thrown even when the `throw` argument is set to `false`. +> **Warning** +> If all of the requests fail because of a connection issue, a `Illuminate\Http\Client\ConnectionException` will still be thrown even when the `throw` argument is set to `false`. ### Error Handling @@ -229,13 +231,16 @@ Unlike Guzzle's default behavior, Laravel's HTTP client wrapper does not throw e 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` or `throwIf` methods: - $response = Http::post(...); + $response = Http::post(/* ... */); // Throw an exception if a client or server error occurred... $response->throw(); // Throw an exception if an error occurred and the given condition is true... $response->throwIf($condition); + + // Throw an exception if an error occurred and the given condition is false... + $response->throwUnless($condition); return $response['user']['id']; @@ -243,14 +248,47 @@ The `Illuminate\Http\Client\RequestException` instance has a public `$response` The `throw` method returns the response instance if no error occurred, allowing you to chain other operations onto the `throw` method: - return Http::post(...)->throw()->json(); + 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: - return Http::post(...)->throw(function ($response, $e) { + return Http::post(/* ... */)->throw(function ($response, $e) { // })->json(); + +### Guzzle Middleware + +Since Laravel's HTTP client is powered by Guzzle, you may take advantage of [Guzzle Middleware](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html) to manipulate the outgoing request or inspect the incoming response. To manipulate the outgoing request, register a Guzzle middleware via the `withMiddleware` method in combination with Guzzle's `mapRequest` middleware factory: + + use GuzzleHttp\Middleware; + use Illuminate\Support\Facades\Http; + use Psr\Http\Message\RequestInterface; + + $response = Http::withMiddleware( + Middleware::mapRequest(function (RequestInterface $request) { + $request->withHeader('X-Example', 'Value'); + + return $request; + }) + ->get('http://example.com'); + +Likewise, you can inspect the incoming HTTP response by registering a middleware via the `withMiddleware` method in combination with Guzzle's `mapResponse` middleware factory: + + use GuzzleHttp\Middleware; + use Illuminate\Support\Facades\Http; + use Psr\Http\Message\ResponseInterface; + + $response = Http::withMiddleware( + Middleware::mapResponse(function (ResponseInterface $response) { + $header = $response->getHeader('X-Example'); + + // ... + + return $response; + }) + )->get('http://example.com'); + ### Guzzle Options @@ -336,7 +374,7 @@ For example, to instruct the HTTP client to return empty, `200` status code resp Http::fake(); - $response = Http::post(...); + $response = Http::post(/* ... */); #### Faking Specific URLs @@ -374,7 +412,7 @@ Sometimes you may need to specify that a single URL should return a series of fa ->pushStatus(404), ]); -When all of the responses in a response sequence have been consumed, any further requests will cause the response sequence to throw an exception. If you would like to specify a default response that should be returned when a sequence is empty, you may use the `whenEmpty` method: +When all the responses in a response sequence have been consumed, any further requests will cause the response sequence to throw an exception. If you would like to specify a default response that should be returned when a sequence is empty, you may use the `whenEmpty` method: Http::fake([ // Stub a series of responses for GitHub endpoints... @@ -474,6 +512,45 @@ Or, you may use the `assertNothingSent` method to assert that no requests were s Http::assertNothingSent(); + +#### Recording Requests / Responses + +You may use the `recorded` method to gather all requests and their corresponding responses. The `recorded` method returns a collection of arrays that contains instances of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response`: + +```php +Http::fake([ + 'https://laravel.com' => Http::response(status: 500), + 'https://nova.laravel.com/' => Http::response(), +]); + +Http::get('https://laravel.com'); +Http::get('https://nova.laravel.com/'); + +$recorded = Http::recorded(); + +[$request, $response] = $recorded[0]; +``` + +Additionally, the `recorded` method accepts a closure which will receive an instance of `Illuminate\Http\Client\Request` and `Illuminate\Http\Client\Response` and may be used to filter request / response pairs based on your expectations: + +```php +use Illuminate\Http\Client\Request; +use Illuminate\Http\Client\Response; + +Http::fake([ + 'https://laravel.com' => Http::response(status: 500), + 'https://nova.laravel.com/' => Http::response(), +]); + +Http::get('https://laravel.com'); +Http::get('https://nova.laravel.com/'); + +$recorded = Http::recorded(function (Request $request, Response $response) { + return $request->url() !== 'https://laravel.com' && + $response->successful(); +}); +``` + ## Events diff --git a/http-tests.md b/http-tests.md index a1356d6ae3f..2607a78fc9f 100644 --- a/http-tests.md +++ b/http-tests.md @@ -78,7 +78,8 @@ Instead of returning an `Illuminate\Http\Response` instance, test request method In general, each of your tests should only make one request to your application. Unexpected behavior may occur if multiple requests are executed within a single test method. -> {tip} For convenience, the CSRF middleware is automatically disabled when running tests. +> **Note** +> For convenience, the CSRF middleware is automatically disabled when running tests. ### Customizing Request Headers @@ -151,7 +152,7 @@ Laravel provides several helpers for interacting with the session during HTTP te } } -Laravel's session is typically used to maintain state for the currently authenticated user. Therefore, the `actingAs` helper method provides a simple way to authenticate a given user as the current user. For example, we may use a [model factory](/docs/{{version}}/database-testing#writing-factories) to generate and authenticate a user: +Laravel's session is typically used to maintain state for the currently authenticated user. Therefore, the `actingAs` helper method provides a simple way to authenticate a given user as the current user. For example, we may use a [model factory](/docs/{{version}}/eloquent-factories) to generate and authenticate a user: actingAs($user, 'web') @@ -278,7 +279,8 @@ In addition, JSON response data may be accessed as array variables on the respon $this->assertTrue($response['created']); -> {tip} The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. +> **Note** +> The `assertJson` method converts the response to an array and utilizes `PHPUnit::assertArraySubset` to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present. #### Asserting Exact JSON Matches @@ -362,6 +364,7 @@ Laravel also offers a beautiful way to fluently test your application's JSON res ->assertJson(fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') + ->whereNot('status', 'pending') ->missing('password') ->etc() ); @@ -373,6 +376,8 @@ In the example above, you may have noticed we invoked the `etc` method at the en The intention behind this behavior is to protect you from unintentionally exposing sensitive information in your JSON responses by forcing you to either explicitly make an assertion against the attribute or explicitly allow additional attributes via the `etc` method. +However, you should be aware that not including the `etc` method in your assertion chain does not ensure that additional attributes are not being added to arrays that are nested within your JSON object. The `etc` method only ensures that no additional attributes exist at the nesting level in which the `etc` method is invoked. + #### Asserting Attribute Presence / Absence @@ -586,7 +591,7 @@ If necessary, you may use the `blade` method to evaluate and render a raw [Blade $view->assertSee('Taylor'); -You may use the `component` method to evaluate and render a [Blade component](/docs/{{version}}/blade#components). Like the `view` method, the `component` method returns an instance of `Illuminate\Testing\TestView`: +You may use the `component` method to evaluate and render a [Blade component](/docs/{{version}}/blade#components). The `component` method returns an instance of `Illuminate\Testing\TestComponent`: $view = $this->component(Profile::class, ['name' => 'Taylor']); @@ -634,6 +639,7 @@ Laravel's `Illuminate\Testing\TestResponse` class provides a variety of custom a [assertJsonMissingExact](#assert-json-missing-exact) [assertJsonMissingValidationErrors](#assert-json-missing-validation-errors) [assertJsonPath](#assert-json-path) +[assertJsonMissingPath](#assert-json-missing-path) [assertJsonStructure](#assert-json-structure) [assertJsonValidationErrors](#assert-json-validation-errors) [assertJsonValidationErrorFor](#assert-json-validation-error-for) @@ -694,7 +700,7 @@ Assert that the response contains the given cookie and it is not expired: #### assertCookieMissing -Assert that the response does not contains the given cookie: +Assert that the response does not contain the given cookie: $response->assertCookieMissing($cookieName); @@ -812,7 +818,8 @@ Assert that the response has no JSON validation errors for the given keys: $response->assertJsonMissingValidationErrors($keys); -> {tip} The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. +> **Note** +> The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. #### assertJsonPath @@ -821,9 +828,9 @@ Assert that the response contains the given data at the specified path: $response->assertJsonPath($path, $expectedValue); -For example, if the JSON response returned by your application contains the following data: +For example, if the following JSON response is returned by your application: -```js +```json { "user": { "name": "Steve Schoger" @@ -835,6 +842,27 @@ You may assert that the `name` property of the `user` object matches a given val $response->assertJsonPath('user.name', 'Steve Schoger'); + +#### assertJsonMissingPath + +Assert that the response does not contain the given path: + + $response->assertJsonMissingPath($path); + +For example, if the following JSON response is returned by your application: + +```json +{ + "user": { + "name": "Steve Schoger" + } +} +``` + +You may assert that it does not contain the `email` property of the `user` object: + + $response->assertJsonMissingPath('user.email'); + #### assertJsonStructure @@ -844,7 +872,7 @@ Assert that the response has a given JSON structure: For example, if the JSON response returned by your application contains the following data: -```js +```json { "user": { "name": "Steve Schoger" @@ -862,7 +890,7 @@ You may assert that the JSON structure matches your expectations like so: Sometimes, JSON responses returned by your application may contain arrays of objects: -```js +```json { "user": [ { @@ -898,7 +926,8 @@ Assert that the response has the given JSON validation errors for the given keys $response->assertJsonValidationErrors(array $data, $responseKey = 'errors'); -> {tip} The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. +> **Note** +> The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. #### assertJsonValidationErrorFor @@ -1050,7 +1079,8 @@ Or, you may assert that a given field has a particular validation error message: 'name' => 'The given name was invalid.' ]); -> {tip} The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. +> **Note** +> The more generic [assertInvalid](#assert-invalid) method may be used to assert that a response has validation errors returned as JSON **or** that errors were flashed to session storage. #### assertSessionHasErrorsIn @@ -1073,7 +1103,8 @@ Assert that the session has no validation errors for the given keys: $response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default'); -> {tip} The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. +> **Note** +> The more generic [assertValid](#assert-valid) method may be used to assert that a response does not have validation errors that were returned as JSON **and** that no errors were flashed to session storage. #### assertSessionMissing diff --git a/installation.md b/installation.md index dea895d1500..b5fc846d199 100644 --- a/installation.md +++ b/installation.md @@ -3,14 +3,14 @@ - [Meet Laravel](#meet-laravel) - [Why Laravel?](#why-laravel) - [Your First Laravel Project](#your-first-laravel-project) +- [Laravel & Docker](#laravel-and-docker) - [Getting Started On macOS](#getting-started-on-macos) - [Getting Started On Windows](#getting-started-on-windows) - [Getting Started On Linux](#getting-started-on-linux) - [Choosing Your Sail Services](#choosing-your-sail-services) - - [Installation Via Composer](#installation-via-composer) - [Initial Configuration](#initial-configuration) - [Environment Based Configuration](#environment-based-configuration) - - [Directory Configuration](#directory-configuration) + - [Databases & Migrations](#databases-and-migrations) - [Next Steps](#next-steps) - [Laravel The Full Stack Framework](#laravel-the-fullstack-framework) - [Laravel The API Backend](#laravel-the-api-backend) @@ -48,13 +48,46 @@ Laravel combines the best packages in the PHP ecosystem to offer the most robust ## 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 local machine. While you may wish to explore these options at a later time, Laravel provides [Sail](/docs/{{version}}/sail), a built-in solution for running your Laravel project using [Docker](https://www.docker.com). +Before creating your first Laravel project, you should ensure that your local machine has PHP and [Composer](https://getcomposer.org) installed. If you are developing on macOS, PHP and Composer can be installed via [Homebrew](https://brew.sh/). In addition, we recommend [installing Node and NPM](https://nodejs.org). + +After you have installed PHP and Composer, you may create a new Laravel project via the Composer `create-project` command: + +```nothing +composer create-project laravel/laravel example-app +``` + +Or, you may create new Laravel projects by globally installing the Laravel installer via Composer: + +```nothing +composer global require laravel/installer + +laravel new example-app +``` + +After the project has been created, start Laravel's local development server using the Laravel's Artisan CLI `serve` command: + +```nothing +cd example-app + +php artisan serve +``` + +Once you have started the Artisan development server, your application will be accessible in your web browser at `http://localhost:8000`. Next, you're ready to [start taking your next steps into the Laravel ecosystem](#next-steps). Of course, you may also want to [configure a database](#databases-and-migrations). + +> **Note** +> If you would like a head start when developing your Laravel application, consider using one of our [starter kits](/docs/{{version}}/starter-kits). Laravel's starter kits provide backend and frontend authentication scaffolding for your new Laravel application. + + +## Laravel & Docker + +We want it to be as easy as possible to get started with Laravel regardless of your preferred operating system. So, there are a variety of options for developing and running a Laravel project on your local machine. While you may wish to explore these options at a later time, Laravel provides [Sail](/docs/{{version}}/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 machine'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 local machine. 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 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. +> **Note** +> 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 @@ -67,6 +100,8 @@ curl -s "https://laravel.build/example-app" | bash Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. +Sail installation may take several minutes while Sail's application containers are built on your local machine. + 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: ```shell @@ -75,18 +110,18 @@ cd example-app ./vendor/bin/sail up ``` -The first time you run the Sail `up` command, Sail's application containers will be built on your local machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** - Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. -> {tip} To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). +> **Note** +> To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). ### Getting Started On Windows 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). -> {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/). +> **Note** +> 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 "example-app", you may run the following command in your terminal: @@ -96,6 +131,8 @@ curl -s https://laravel.build/example-app | bash Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. +Sail installation may take several minutes while Sail's application containers are built on your local machine. + 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: ```shell @@ -104,11 +141,10 @@ cd example-app ./vendor/bin/sail up ``` -The first time you run the Sail `up` command, Sail's application containers will be built on your local machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** - Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. -> {tip} To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). +> **Note** +> To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). #### Developing Within WSL2 @@ -127,6 +163,8 @@ curl -s https://laravel.build/example-app | bash Of course, you can change "example-app" in this URL to anything you like - just make sure the application name only contains alpha-numeric characters, dashes, and underscores. The Laravel application's directory will be created within the directory you execute the command from. +Sail installation may take several minutes while Sail's application containers are built on your local machine. + 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: ```shell @@ -135,11 +173,10 @@ cd example-app ./vendor/bin/sail up ``` -The first time you run the Sail `up` command, Sail's application containers will be built on your local machine. This could take several minutes. **Don't worry, subsequent attempts to start Sail will be much faster.** - Once the application's Docker containers have been started, you can access the application in your web browser at: http://localhost. -> {tip} To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). +> **Note** +> To continue learning more about Laravel Sail, review its [complete documentation](/docs/{{version}}/sail). ### Choosing Your Sail Services @@ -158,97 +195,52 @@ You may instruct Sail to install a default [Devcontainer](/docs/{{version}}/sail curl -s "https://laravel.build/example-app?with=mysql,redis&devcontainer" | bash ``` - -### Installation Via Composer - -If your local machine 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: - -```shell -composer create-project laravel/laravel example-app - -cd example-app - -php artisan serve -``` - -Once you have started the Artisan development server, you may access your application at `http://localhost:8000`. - - -#### The Laravel Installer - -Or, you may install the Laravel Installer as a global Composer dependency: - -```shell -composer global require laravel/installer - -laravel new example-app - -cd example-app - -php artisan serve -``` + +## Initial Configuration -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: +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. -
+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. -- 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` + +### Environment Based Configuration -
+Since many of Laravel's configuration option values may vary depending on whether your application is running on your local machine or on a production web server, many important configuration values are defined using the `.env` file that exists at the root of your application. -For convenience, the Laravel installer can also create a Git repository for your new project. To indicate that you want a Git repository to be created, pass the `--git` flag when creating a new project: +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. -```shell -laravel new example-app --git -``` +> **Note** +> For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). -This command will initialize a new Git repository for your project and automatically commit the base Laravel skeleton. The `git` flag assumes you have properly installed and configured Git. You can also use the `--branch` flag to set the initial branch name: + +### Databases & Migrations -```shell -laravel new example-app --git --branch="main" -``` +Now that you have created your Laravel application, you probably want to store some data in a database. By default, your application's `.env` configuration file specifies that Laravel will be interacting with a MySQL database and will access the database at `127.0.0.1`. If you are developing on macOS and need to install MySQL, Postgres, or Redis locally, you may find it convenient to utilize [DBngin](https://dbngin.com/). -Instead of using the `--git` flag, you may also use the `--github` flag to create a Git repository and also create a corresponding private repository on GitHub: +If you do not want to install MySQL or Postgres on your local machine, you can always use a [SQLite](https://www.sqlite.org/index.html) database. SQLite is a small, fast, self-contained database engine. To get started, create a SQLite database by creating an empty SQLite file. Typically, this file will exist within the `database` directory of your Laravel application: ```shell -laravel new example-app --github +touch database/database.sqlite ``` -The created repository will then be available at `https://github.com//example-app`. The `github` flag assumes you have properly installed the [GitHub CLI](https://cli.github.com) and are authenticated with GitHub. Additionally, you should have `git` installed and properly configured. If needed, you can pass additional flags that are supported by the GitHub CLI: +Next, update your `.env` configuration file to use Laravel's `sqlite` database driver. You may remove the other database configuration options: -```shell -laravel new example-app --github="--public" +```ini +DB_CONNECTION=sqlite # [tl! add] +DB_CONNECTION=mysql # [tl! remove] +DB_HOST=127.0.0.1 # [tl! remove] +DB_PORT=3306 # [tl! remove] +DB_DATABASE=laravel # [tl! remove] +DB_USERNAME=root # [tl! remove] +DB_PASSWORD= # [tl! remove] ``` -You may use the `--organization` flag to create the repository under a specific GitHub organization: +Once you have configured your SQLite database, you may run your application's [database migrations](/docs/{{version}}/migrations), which will create your application's database tables: ```shell -laravel new example-app --github="--public" --organization="laravel" +php artisan migrate ``` - -## Initial Configuration - -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. - -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. - - -### Environment Based Configuration - -Since many of Laravel's configuration option values may vary depending on whether your application is running on your local machine 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. - -> {tip} For more information about the `.env` file and environment based configuration, check out the full [configuration documentation](/docs/{{version}}/configuration#environment-configuration). - - -### Directory Configuration - -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 that exist within your application. - ## Next Steps @@ -259,6 +251,7 @@ Now that you have created your Laravel project, you may be wondering what to lea - [Request Lifecycle](/docs/{{version}}/lifecycle) - [Configuration](/docs/{{version}}/configuration) - [Directory Structure](/docs/{{version}}/structure) +- [Frontend](/docs/{{version}}/frontend) - [Service Container](/docs/{{version}}/container) - [Facades](/docs/{{version}}/facades) @@ -269,13 +262,14 @@ How you want to use Laravel will also dictate the next steps on your journey. Th ### 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 a single-page application hybrid technology like [Inertia.js](https://inertiajs.com). This is the most common way to use the Laravel framework, and, in our opinion, the most productive way to use Laravel. +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 a single-page application hybrid technology like [Inertia](https://inertiajs.com). This is the most common way to use the Laravel framework, and, in our opinion, the most productive way to use Laravel. -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 this is how you plan to use Laravel, you may want to check out our documentation on [frontend development](/docs/{{version}}/frontend), [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](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). +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 [Vite](/docs/{{version}}/vite). -> {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). +> **Note** +> 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 @@ -284,4 +278,5 @@ Laravel may also serve as an API backend to a JavaScript single-page application 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). -> {tip} Need a head start scaffolding your Laravel backend and Next.js frontend? Laravel Breeze offers an [API stack](/docs/{{version}}/starter-kits#breeze-and-next) as well as a [Next.js frontend implementation](https://github.com/laravel/breeze-next) so you can get started in minutes. +> **Note** +> Need a head start scaffolding your Laravel backend and Next.js frontend? Laravel Breeze offers an [API stack](/docs/{{version}}/starter-kits#breeze-and-next) as well as a [Next.js frontend implementation](https://github.com/laravel/breeze-next) so you can get started in minutes. diff --git a/localization.md b/localization.md index a934480d061..784b53dc382 100644 --- a/localization.md +++ b/localization.md @@ -24,7 +24,7 @@ Laravel provides two ways to manage translation strings. First, language strings /es messages.php -Or, translation strings may be defined within JSON files that are placed within the `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: +Or, translation strings may be defined within JSON files that are placed within the `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 applications that have a large number of translatable strings: /lang en.json @@ -87,7 +87,8 @@ You may instruct Laravel's "pluralizer", which is used by Eloquent and other por // ... } -> {note} If you customize the pluralizer's language, you should explicitly define your Eloquent model's [table names](/docs/{{version}}/eloquent#table-names). +> **Warning** +> If you customize the pluralizer's language, you should explicitly define your Eloquent model's [table names](/docs/{{version}}/eloquent#table-names). ## Defining Translation Strings @@ -113,7 +114,8 @@ All language files return an array of keyed strings. For example: '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". +> **Warning** +> 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". ### Using Translation Strings As Keys diff --git a/logging.md b/logging.md index 7b9783b5ccd..691deb6ed87 100644 --- a/logging.md +++ b/logging.md @@ -59,7 +59,8 @@ 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](#monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. +> **Note** +> Check out the documentation on [advanced channel customization](#monolog-channel-customization) to learn more about the `monolog` and `custom` drivers. ### Channel Prerequisites @@ -336,7 +337,8 @@ Once you have configured the `tap` option on your channel, you're ready to defin } } -> {tip} All of your "tap" classes are resolved by the [service container](/docs/{{version}}/container), so any constructor dependencies they require will automatically be injected. +> **Note** +> All of your "tap" classes are resolved by the [service container](/docs/{{version}}/container), so any constructor dependencies they require will automatically be injected. ### Creating Monolog Handler Channels @@ -406,6 +408,6 @@ Once you have configured the `custom` driver channel, you're ready to define the */ public function __invoke(array $config) { - return new Logger(...); + return new Logger(/* ... */); } } diff --git a/mail.md b/mail.md index 6bfa50c767d..42afed52d6f 100644 --- a/mail.md +++ b/mail.md @@ -11,6 +11,7 @@ - [View Data](#view-data) - [Attachments](#attachments) - [Inline Attachments](#inline-attachments) + - [Attachable Objects](#attachable-objects) - [Tags & Metadata](#tags-and-metadata) - [Customizing The Symfony Message](#customizing-the-symfony-message) - [Markdown Mailables](#markdown-mailables) @@ -171,7 +172,8 @@ php artisan make:mail OrderShipped 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. -> {tip} You may type-hint dependencies on the mailable's `build` method. The Laravel [service container](/docs/{{version}}/container) automatically injects these dependencies. +> **Note** +> You may type-hint dependencies on the mailable's `build` method. The Laravel [service container](/docs/{{version}}/container) automatically injects these dependencies. ### Configuring The Sender @@ -218,7 +220,8 @@ Within a mailable class' `build` method, you may use the `view` method to specif return $this->view('emails.orders.shipped'); } -> {tip} You may wish to create a `resources/views/emails` directory to house all of your email templates; however, you are free to place them wherever you wish within your `resources/views` directory. +> **Note** +> You may wish to create a `resources/views/emails` directory to house all of your email templates; however, you are free to place them wherever you wish within your `resources/views` directory. #### Plain Text Emails @@ -456,7 +459,8 @@ Embedding inline images into your emails is typically cumbersome; however, Larav ``` -> {note} The `$message` variable is not available in plain-text message templates since plain-text messages do not utilize inline attachments. +> **Warning** +> The `$message` variable is not available in plain-text message templates since plain-text messages do not utilize inline attachments. #### Embedding Raw Data Attachments @@ -471,6 +475,65 @@ If you already have a raw image data string you wish to embed into an email temp ``` + +### Attachable Objects + +While attaching files to messages via simple string paths is often sufficient, in many cases the attachable entities within your application are represented by classes. For example, if your application is attaching a photo to a message, your application may also have a `Photo` model that represents that photo. When that is the case, wouldn't it be convenient to simply pass the `Photo` model to the `attach` method? Attachable objects allow you to do just that. + +To get started, implement the `Illuminate\Contracts\Mail\Attachable` interface on the object that will be attachable to messages. This interface dictates that your class defines a `toMailAttachment` method that returns an `Illuminate\Mail\Attachment` instance: + + view('photos.resized') + ->attach($this->photo); + } + +Of course, attachment data may be stored on a remote file storage service such as Amazon S3. So, Laravel also allows you to generate attachment instances from data that is stored on one of your application's [filesystem disks](/docs/{{version}}/filesystem): + + // Create an attachment from a file on your default disk... + return Attachment::fromStorage($this->path); + + // Create an attachment from a file on a specific disk... + return Attachment::fromStorageDisk('backblaze', $this->path); + +In addition, you may create attachment instances via data that you have in memory. To accomplish this, provide a closure to the `fromData` method. The closure should return the raw data that represents the attachment: + + return Attachment::fromData(fn () => $this->content, 'Photo Name'); + +Laravel also provides additional methods that you may use to customize your attachments. For example, you may use the `as` and `withMime` methods to customize the file's name and MIME type: + + return Attachment::fromPath('/path/to/file') + ->as('Photo Name') + ->withMime('image/jpeg'); + ### Tags & Metadata @@ -566,7 +629,8 @@ Thanks,
@endcomponent ``` -> {tip} Do not use excess indentation when writing Markdown emails. Per Markdown standards, Markdown parsers will render indented content as code blocks. +> **Note** +> Do not use excess indentation when writing Markdown emails. Per Markdown standards, Markdown parsers will render indented content as code blocks. #### Button Component @@ -770,7 +834,8 @@ Alternatively, you may call the `afterCommit` method from your mailable's constr } } -> {tip} To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). +> **Note** +> To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). ## Rendering Mailables @@ -795,7 +860,8 @@ When designing a mailable's template, it is convenient to quickly preview the re return new App\Mail\InvoicePaid($invoice); }); -> {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). +> **Warning** +> [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). ## Localizing Mailables @@ -987,7 +1053,7 @@ Once you've defined your custom transport, you may register it via the `extend` public function boot() { Mail::extend('mailchimp', function (array $config = []) { - return new MailchimpTransport(...); + return new MailchimpTransport(/* ... */); }) } diff --git a/middleware.md b/middleware.md index e911ce65e90..aa66de2de4f 100644 --- a/middleware.md +++ b/middleware.md @@ -57,7 +57,8 @@ As you can see, if the given `token` does not match our secret token, the middle 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. +> **Note** +> 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. @@ -215,7 +216,8 @@ Middleware groups may be assigned to routes and controller actions using the sam // }); -> {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`. +> **Note** +> 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 diff --git a/migrations.md b/migrations.md index 69839e37bdc..8120e92fc27 100644 --- a/migrations.md +++ b/migrations.md @@ -43,7 +43,8 @@ Laravel will use the name of the migration to attempt to guess the name of the t 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. -> {tip} Migration stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). +> **Note** +> Migration stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). ### Squashing Migrations @@ -61,7 +62,8 @@ When you execute this command, Laravel will write a "schema" file to your applic 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 and utilizes the database's command-line client. Schema dumps may not be restored to in-memory SQLite databases. +> **Warning** +> Migration squashing is only available for the MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client. Schema dumps may not be restored to in-memory SQLite databases. ## Migration Structure @@ -141,6 +143,12 @@ If you would like to see which migrations have run thus far, you may use the `mi php artisan migrate:status ``` +If you would like to see the SQL statements that will be executed by the migrations without actually running them, you may provide the `--pretend` flag to the `migrate` command: + +```shell +php artisan migrate --pretend +``` + #### Forcing Migrations To Run In Production @@ -200,7 +208,8 @@ php artisan migrate:fresh php artisan migrate:fresh --seed ``` -> {note} The `migrate:fresh` command will drop all database tables regardless of their prefix. This command should be used with caution when developing on a database that is shared with other applications. +> **Warning** +> The `migrate:fresh` command will drop all database tables regardless of their prefix. This command should be used with caution when developing on a database that is shared with other applications. ## Tables @@ -269,6 +278,14 @@ The `temporary` method may be used to indicate that the table should be "tempora // ... }); +If you would like to add a "comment" to a database table, you may invoke the `comment` method on the table instance. Table comments are currently only supported by MySQL and Postgres: + + Schema::create('calculations', function (Blueprint $table) { + $table->comment('Business calculations'); + + // ... + }); + ### Updating Tables @@ -927,7 +944,8 @@ The `default` modifier accepts a value or an `Illuminate\Database\Query\Expressi } }; -> {note} Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. +> **Warning** +> Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation. In addition, it is not possible to combine raw `default` expressions (using `DB::raw`) with column changes via the `change` method. #### Column Order @@ -962,7 +980,8 @@ use Illuminate\Database\DBAL\TimestampType; ], ``` -> {note} If your application is using Microsoft SQL Server, please ensure that you install `doctrine/dbal:^3.0`. +> **Warning** +> If your application is using Microsoft SQL Server, please ensure that you install `doctrine/dbal:^3.0`. #### Updating Column Attributes @@ -979,7 +998,8 @@ We could also modify a column to be nullable: $table->string('name', 50)->nullable()->change(); }); -> {note} The following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). +> **Warning** +> The following column types can be modified: `bigInteger`, `binary`, `boolean`, `char`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`. To modify a `timestamp` column type a [Doctrine type must be registered](#prerequisites). #### Renaming Columns @@ -990,7 +1010,8 @@ To rename a column, you may use the `renameColumn` method provided by the schema $table->renameColumn('from', 'to'); }); -> {note} Renaming an `enum` column is not currently supported. +> **Warning** +> Renaming an `enum` column is not currently supported. ### Dropping Columns @@ -1007,7 +1028,8 @@ You may drop multiple columns from a table by passing an array of column names t $table->dropColumn(['votes', 'avatar', 'location']); }); -> {note} Dropping or modifying multiple columns within a single migration while using an SQLite database is not supported. +> **Warning** +> Dropping or modifying multiple columns within a single migration while using an SQLite database is not supported. #### Available Command Aliases @@ -1179,7 +1201,8 @@ You may enable or disable foreign key constraints within your migrations by usin Schema::disableForeignKeyConstraints(); -> {note} SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. In addition, SQLite only supports foreign keys upon creation of the table and [not when tables are altered](https://www.sqlite.org/omitted.html). +> **Warning** +> SQLite disables foreign key constraints by default. When using SQLite, make sure to [enable foreign key support](/docs/{{version}}/database#configuration) in your database configuration before attempting to create them in your migrations. In addition, SQLite only supports foreign keys upon creation of the table and [not when tables are altered](https://www.sqlite.org/omitted.html). ## Events @@ -1194,4 +1217,3 @@ For convenience, each migration operation will dispatch an [event](/docs/{{versi | `Illuminate\Database\Events\MigrationEnded` | A single migration has finished executing. | | `Illuminate\Database\Events\SchemaDumped` | A database schema dump has completed. | | `Illuminate\Database\Events\SchemaLoaded` | An existing database schema dump has loaded. | - diff --git a/mix.md b/mix.md index 33bea22bf20..803a3181ceb 100644 --- a/mix.md +++ b/mix.md @@ -1,23 +1,6 @@ -# Compiling Assets (Mix) +# Laravel Mix - [Introduction](#introduction) -- [Installation & Setup](#installation) -- [Running Mix](#running-mix) -- [Working With Stylesheets](#working-with-stylesheets) - - [Tailwind CSS](#tailwindcss) - - [PostCSS](#postcss) - - [Sass](#sass) - - [URL Processing](#url-processing) - - [Source Maps](#css-source-maps) -- [Working With JavaScript](#working-with-scripts) - - [Vue](#vue) - - [React](#react) - - [Vendor Extraction](#vendor-extraction) - - [Custom Webpack Configuration](#custom-webpack-configuration) -- [Versioning / Cache Busting](#versioning-and-cache-busting) -- [Browsersync Reloading](#browsersync-reloading) -- [Environment Variables](#environment-variables) -- [Notifications](#notifications) ## Introduction @@ -33,402 +16,5 @@ mix.js('resources/js/app.js', 'public/js') 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). - - -## Installation & Setup - - -#### Installing Node - -Before running Mix, you must first ensure that Node.js and NPM are installed on your machine: - -```shell -node -v -npm -v -``` - -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}}/sail), you may invoke Node and NPM through Sail: - -```shell -./sail node -v -./sail npm -v -``` - - -#### 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 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: - -```shell -npm install -``` - - -## 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 are 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: - -```shell -// Run all Mix tasks... -npm run dev - -// Run all Mix tasks and minify output... -npm run prod -``` - - -#### Watching Assets For Changes - -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: - -```shell -npm run watch -``` - -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: - -```shell -npm run watch-poll -``` - - -## Working With Stylesheets - -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. - - -### 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: - -```shell -npm install - -npm install -D tailwindcss - -npx tailwindcss init -``` - -The `init` command will generate a `tailwind.config.js` file. The `content` section of this file allows you to configure the paths to all of your HTML templates, JavaScript components, and any other source files that contain Tailwind class names so that any CSS classes that are not used within these files will be purged from your production CSS build: - -```js -content: [ - './storage/framework/views/*.php', - './resources/**/*.blade.php', - './resources/**/*.js', - './resources/**/*.vue', - "./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php", -], -``` - -Next, you should add each of Tailwind's "layers" to your application's `resources/css/app.css` file: - -```css -@tailwind base; -@tailwind components; -@tailwind utilities; -``` - -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: - -```js -mix.js('resources/js/app.js', 'public/js') - .postCss('resources/css/app.css', 'public/css', [ - require('tailwindcss'), - ]); -``` - -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: - -```blade - - - - - -``` - - -### 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) plugin to automatically apply all necessary CSS3 vendor prefixes. However, you're free to add any additional plugins that are appropriate for your application. - -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: - -```js -mix.postCss('resources/css/app.css', 'public/css', [ - require('postcss-custom-properties') -]); -``` - -Or, you may execute `postCss` with no additional plugins in order to achieve simple CSS compilation and minification: - -```js -mix.postCss('resources/css/app.css', 'public/css'); -``` - - -### Sass - -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: - -```js -mix.sass('resources/sass/app.scss', 'public/css'); -``` - -You 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: - -```js -mix.sass('resources/sass/app.sass', 'public/css') - .sass('resources/sass/admin.sass', 'public/css/admin'); -``` - - -### 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: - -```css -.example { - background: url('../images/example.png'); -} -``` - -> {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: - -```css -.example { - background: url(/images/example.png?d41d8cd98f00b204e9800998ecf8427e); -} -``` - -As useful as this feature may be, your existing folder structure may already be configured in a way you like. If this is the case, you may disable `url()` rewriting like so: - -```js -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: - -```css -.example { - background: url("../images/thing.png"); -} -``` - - -### 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: - -```js -mix.js('resources/js/app.js', 'public/js') - .sourceMaps(); -``` - - -#### Style Of Source Mapping - -Webpack offers a variety of [source mapping styles](https://webpack.js.org/configuration/devtool/#devtool). By default, Mix's source mapping style is set to `eval-source-map`, which provides a fast rebuild time. If you want to change the mapping style, you may do so using the `sourceMaps` method: - -```js -let productionSourceMaps = false; - -mix.js('resources/js/app.js', 'public/js') - .sourceMaps(productionSourceMaps, 'source-map'); -``` - - -## Working With JavaScript - -Mix provides several features to help you work with your JavaScript files, such as compiling modern ECMAScript, module bundling, minification, and concatenating plain JavaScript files. Even better, this all works seamlessly, without requiring an ounce of custom configuration: - -```js -mix.js('resources/js/app.js', 'public/js'); -``` - -With this single line of code, you may now take advantage of: - -
- -- The latest EcmaScript syntax. -- Modules -- Minification for production environments. - -
- - -### Vue - -Mix will automatically install the Babel plugins necessary for Vue single-file component compilation support when using the `vue` method. No further configuration is required: - -```js -mix.js('resources/js/app.js', 'public/js') - .vue(); -``` - -Once your JavaScript has been compiled, you can reference it in your application: - -```blade - - - - - -``` - - -### React - -Mix can automatically install the Babel plugins necessary for React support. To get started, add a call to the `react` method: - -```js -mix.js('resources/js/app.jsx', 'public/js') - .react(); -``` - -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: - -```blade - - - - - -``` - - -### Vendor Extraction - -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: - -```js -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 snippet above as an example, Mix will generate the following files: - -
- -- `public/js/manifest.js`: *The Webpack manifest runtime* -- `public/js/vendor.js`: *Your vendor libraries* -- `public/js/app.js`: *Your application code* - -
- -To avoid JavaScript errors, be sure to load these files in the proper order: - -```html - - - -``` - - -### Custom Webpack Configuration - -Occasionally, you may need to manually modify the underlying Webpack configuration. For example, you might have a special loader or plugin that needs to be referenced. - -Mix provides a useful `webpackConfig` method that allows you to merge any short Webpack configuration overrides. This is particularly appealing, as it doesn't require you to copy and maintain your own copy of the `webpack.config.js` file. The `webpackConfig` method accepts an object, which should contain any [Webpack-specific configuration](https://webpack.js.org/configuration/) that you wish to apply. - -```js -mix.webpackConfig({ - resolve: { - modules: [ - path.resolve(__dirname, 'vendor/laravel/spark/resources/assets/js') - ] - } -}); -``` - - -## 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 automatically handle this for you using the `version` method. - -The `version` method will append a unique hash to the filenames of all compiled files, allowing for more convenient cache busting: - -```js -mix.js('resources/js/app.js', 'public/js') - .version(); -``` - -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: - -```blade - -``` - -Because versioned files are usually unnecessary in development, you may instruct the versioning process to only run during `npm run prod`: - -```js -mix.js('resources/js/app.js', 'public/js'); - -if (mix.inProduction()) { - mix.version(); -} -``` - - -#### 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 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: - -```shell -https://cdn.example.com/js/app.js?id=1964becbdd96414518cd -``` - - -## 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 for this by calling the `mix.browserSync()` method: - -```js -mix.browserSync('laravel.test'); -``` - -[BrowserSync options](https://browsersync.io/docs/options) may be specified by passing a JavaScript object to the `browserSync` method: - -```js -mix.browserSync({ - proxy: 'laravel.test' -}); -``` - -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. - - -## Environment Variables - -You may inject environment variables into your `webpack.mix.js` script by prefixing one of the environment variables in your `.env` file with `MIX_`: - -```ini -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. However, you will need to restart the task if the environment variable's value changes while the task is running: - -```js -process.env.MIX_SENTRY_DSN_PUBLIC -``` - - -## Notifications - -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: - -```js -mix.disableNotifications(); -``` +> **Note** +> Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [official Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [Vite migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). diff --git a/mocking.md b/mocking.md index fa65530a1a3..7088307f60c 100644 --- a/mocking.md +++ b/mocking.md @@ -123,7 +123,8 @@ We can mock the call to the `Cache` facade by using the `shouldReceive` method, } } -> {note} You should not mock the `Request` facade. Instead, pass the input you desire into the [HTTP testing methods](/docs/{{version}}/http-tests) such as `get` and `post` when running your test. Likewise, instead of mocking the `Config` facade, call the `Config::set` method in your tests. +> **Warning** +> You should not mock the `Request` facade. Instead, pass the input you desire into the [HTTP testing methods](/docs/{{version}}/http-tests) such as `get` and `post` when running your test. Likewise, instead of mocking the `Config` facade, call the `Config::set` method in your tests. ### Facade Spies @@ -177,7 +178,7 @@ You may use the `Bus` facade's `fake` method to prevent jobs from being dispatch // Assert that a job was dispatched synchronously... Bus::assertDispatchedSync(AnotherJob::class); - // Assert that a job was not dipatched synchronously... + // Assert that a job was not dispatched synchronously... Bus::assertNotDispatchedSync(AnotherJob::class); // Assert that a job was dispatched after the response was sent... @@ -288,7 +289,8 @@ If you would simply like to assert that an event listener is listening to a give SendShipmentNotification::class ); -> {note} After calling `Event::fake()`, no event listeners will be executed. So, if your tests use model factories that rely on events, such as creating a UUID during a model's `creating` event, you should call `Event::fake()` **after** using your factories. +> **Warning** +> After calling `Event::fake()`, no event listeners will be executed. So, if your tests use model factories that rely on events, such as creating a UUID during a model's `creating` event, you should call `Event::fake()` **after** using your factories. #### Faking A Subset Of Events @@ -312,6 +314,12 @@ If you only want to fake event listeners for a specific set of events, you may p $order->update([...]); } +You may fake all events except for a set of specified events using the `fakeExcept` method: + + Event::fakeExcept([ + OrderCreated::class, + ]); + ### Scoped Event Fakes @@ -406,12 +414,15 @@ You may pass a closure to the `assertSent`, `assertNotSent`, `assertQueued`, or return $mail->order->id === $order->id; }); -When calling the `Mail` facade's assertion methods, the mailable instance accepted by the provided closure exposes helpful methods for examining the recipients of the mailable: +When calling the `Mail` facade's assertion methods, the mailable instance accepted by the provided closure exposes helpful methods for examining the mailable: Mail::assertSent(OrderShipped::class, function ($mail) use ($user) { return $mail->hasTo($user->email) && $mail->hasCc('...') && - $mail->hasBcc('...'); + $mail->hasBcc('...') && + $mail->hasReplyTo('...') && + $mail->hasFrom('...') && + $mail->hasSubject('...'); }); You may have noticed that there are two methods for asserting that mail was not sent: `assertNotSent` and `assertNotQueued`. Sometimes you may wish to assert that no mail was sent **or** queued. To accomplish this, you may use the `assertNothingOutgoing` and `assertNotOutgoing` methods: @@ -464,6 +475,9 @@ After calling the `Notification` facade's `fake` method, you may then assert tha Notification::assertNotSentTo( [$user], AnotherNotification::class ); + + // Assert that a given number of notifications were sent... + Notification::assertCount(3); } } @@ -539,6 +553,20 @@ You may pass a closure to the `assertPushed` or `assertNotPushed` methods in ord return $job->order->id === $order->id; }); +If you only need to fake specific jobs while allowing your other jobs to execute normally, you may pass the class names of the jobs that should be faked to the `fake` method: + + public function test_orders_can_be_shipped() + { + Queue::fake([ + ShipOrder::class, + ]); + + // Perform order shipping... + + // Assert a job was pushed twice... + Queue::assertPushed(ShipOrder::class, 2); + } + ### Job Chains @@ -606,7 +634,8 @@ The `Storage` facade's `fake` method allows you to easily generate a fake disk t By default, the `fake` method will delete all files in its temporary directory. If you would like to keep these files, you may use the "persistentFake" method instead. For more information on testing file uploads, you may consult the [HTTP testing documentation's information on file uploads](/docs/{{version}}/http-tests#testing-file-uploads). -> {note} The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php). +> **Warning** +> The `image` method requires the [GD extension](https://www.php.net/manual/en/book.image.php). ## Interacting With Time diff --git a/notifications.md b/notifications.md index 7ccbcf5ae8b..c674575b599 100644 --- a/notifications.md +++ b/notifications.md @@ -52,7 +52,7 @@ ## Introduction -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. +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 notifications 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. @@ -93,7 +93,8 @@ The `notify` method that is provided by this trait expects to receive a notifica $user->notify(new InvoicePaid($invoice)); -> {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. +> **Note** +> Remember, you may use the `Notifiable` trait on any of your models. You are not limited to only including it on your `User` model. ### Using The Notification Facade @@ -113,7 +114,8 @@ You can also send notifications immediately using the `sendNow` method. This met Every notification class has a `via` method that determines on which channels the notification will be delivered. Notifications may be sent on the `mail`, `database`, `broadcast`, `vonage`, and `slack` channels. -> {tip} If you would like to use other delivery channels such as Telegram or Pusher, check out the community driven [Laravel Notification Channels website](http://laravel-notification-channels.com). +> **Note** +> If you would like to use other delivery channels such as Telegram or Pusher, check out the community driven [Laravel Notification Channels website](http://laravel-notification-channels.com). The `via` method receives a `$notifiable` instance, which will be an instance of the class to which the notification is being sent. You may use `$notifiable` to determine which channels the notification should be delivered on: @@ -131,7 +133,8 @@ The `via` method receives a `$notifiable` instance, which will be an instance of ### Queueing Notifications -> {note} Before queueing notifications you should configure your queue and [start a worker](/docs/{{version}}/queues). +> **Warning** +> 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 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: @@ -257,7 +260,8 @@ Alternatively, you may call the `afterCommit` method from your notification's co } } -> {tip} To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). +> **Note** +> To learn more about working around these issues, please review the documentation regarding [queued jobs and database transactions](/docs/{{version}}/queues#jobs-and-database-transactions). #### Determining If A Queued Notification Should Be Sent @@ -283,9 +287,12 @@ However, if you would like to make the final determination on whether the queued 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: + use Illuminate\Broadcasting\Channel; + Notification::route('mail', 'taylor@example.com') ->route('vonage', '5555555555') ->route('slack', 'https://hooks.slack.com/services/...') + ->route('broadcast', [new Channel('channel-name')]) ->notify(new InvoicePaid($invoice)); If you would like to provide the recipient's name when sending an on-demand notification to the `mail` route, you may provide an array that contains the email address as the key and the name as the value of the first element in the array: @@ -317,22 +324,25 @@ The `MailMessage` class contains a few simple methods to help you build transact return (new MailMessage) ->greeting('Hello!') ->line('One of your invoices has been paid!') + ->lineIf($this->amount > 0, "Amount paid: {$this->amount}") ->action('View Invoice', $url) ->line('Thank you for using our application!'); } -> {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. +> **Note** +> 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 beautiful, responsive HTML email template with a plain-text counterpart. Here is an example of an email generated by the `mail` channel: -> {tip} When sending mail notifications, be sure to set the `name` configuration option in your `config/app.php` configuration file. This value will be used in the header and footer of your mail notification messages. +> **Note** +> 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. - -#### Other Mail Notification Formatting Options + +#### Error Messages -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: +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. @@ -342,12 +352,16 @@ Instead of defining the "lines" of text in the notification class, you may use t */ public function toMail($notifiable) { - return (new MailMessage)->view( - 'emails.name', ['invoice' => $this->invoice] - ); + return (new MailMessage) + ->error() + ->subject('Invoice Payment Failed') + ->line('...'); } -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: + +#### 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: /** * Get the mail representation of the notification. @@ -358,15 +372,11 @@ You may specify a plain-text view for the mail message by passing the view name public function toMail($notifiable) { return (new MailMessage)->view( - ['emails.name.html', 'emails.name.plain'], - ['invoice' => $this->invoice] + 'emails.name', ['invoice' => $this->invoice] ); } - -#### 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 black: +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. @@ -376,10 +386,10 @@ Some notifications inform users of errors, such as a failed invoice payment. You */ public function toMail($notifiable) { - return (new MailMessage) - ->error() - ->subject('Notification Subject') - ->line('...'); + return (new MailMessage)->view( + ['emails.name.html', 'emails.name.plain'], + ['invoice' => $this->invoice] + ); } @@ -495,6 +505,9 @@ To add attachments to an email notification, use the `attach` method while build ->attach('/path/to/file'); } +> **Note** +> The `attach` method offered by notification mail messages also accepts [attachable objects](/docs/{{version}}/mail#attachable-objects). Please consult the comprehensive [attachable object documentation](/docs/{{version}}/mail#attachable-objects) to learn more. + When attaching files to a message, you may also specify the display name and / or MIME type by passing an `array` as the second argument to the `attach` method: /** @@ -530,6 +543,27 @@ Unlike attaching files in mailable objects, you may not attach a file directly f ->attachFromStorage('/path/to/file'); } +When necessary, multiple files may be attached to a message using the `attachMany` method: + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->greeting('Hello!') + ->attachMany([ + '/path/to/forge.svg', + '/path/to/vapor.svg' => [ + 'as' => 'Logo.svg', + 'mime' => 'image/svg+xml', + ], + ]); + } + #### Raw Data Attachments @@ -572,7 +606,6 @@ Some third-party email providers such as Mailgun and Postmark support message "t If your application is using the Mailgun driver, you may consult Mailgun's documentation for more information on [tags](https://documentation.mailgun.com/en/latest/user_manual.html#tagging-1) and [metadata](https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages). Likewise, the Postmark documentation may also be consulted for more information on their support for [tags](https://postmarkapp.com/blog/tags-support-for-smtp) and [metadata](https://postmarkapp.com/support/article/1125-custom-metadata-faq). If your application is using Amazon SES to send emails, you should use the `metadata` method to attach [SES "tags"](https://docs.aws.amazon.com/ses/latest/APIReference/API_MessageTag.html) to the message. -Tags and metadata can be added to the `MailMessage` - these are used by your email service for filtering/processing: ### Customizing The Symfony Message @@ -835,7 +868,8 @@ 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 URL from your JavaScript client. +> **Note** +> 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. ### Marking Notifications As Read @@ -1308,7 +1342,8 @@ When a notification is sent, the `Illuminate\Notifications\Events\NotificationSe ], ]; -> {tip} After registering listeners in your `EventServiceProvider`, use the `event:generate` Artisan command to quickly generate listener classes. +> **Note** +> After registering listeners in your `EventServiceProvider`, use the `event:generate` Artisan command to quickly generate listener classes. Within an event listener, you may access the `notifiable`, `notification`, `channel`, and `response` properties on the event to learn more about the notification recipient or the notification itself: diff --git a/octane.md b/octane.md index e660c61182c..6a084e86106 100644 --- a/octane.md +++ b/octane.md @@ -46,7 +46,8 @@ php artisan octane:install ## Server Prerequisites -> {note} Laravel Octane requires [PHP 8.0+](https://php.net/releases/). +> **Warning** +> Laravel Octane requires [PHP 8.0+](https://php.net/releases/). ### RoadRunner @@ -82,7 +83,7 @@ After installing the RoadRunner binary, you may exit your Sail shell session. Yo Next, update the `command` directive of your application's `docker/supervisord.conf` file so that Sail serves your application using Octane instead of the PHP development server: ```ini -command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port=8000 +command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=roadrunner --host=0.0.0.0 --rpc-port=6001 --port=80 ``` Finally, ensure the `rr` binary is executable and build your Sail images: @@ -105,7 +106,8 @@ pecl install swoole #### Swoole Via Laravel Sail -> {note} Before serving an Octane application via Sail, ensure you have the latest version of Laravel Sail and execute `./vendor/bin/sail build --no-cache` within your application's root directory. +> **Warning** +> Before serving an Octane application via Sail, ensure you have the latest version of Laravel Sail and execute `./vendor/bin/sail build --no-cache` within your application's root directory. Alternatively, you may develop your Swoole based Octane application using [Laravel Sail](/docs/{{version}}/sail), the official Docker based development environment for Laravel. Laravel Sail includes the Swoole extension by default. However, you will still need to adjust the `supervisor.conf` file used by Sail to keep your application running. To get started, execute the `sail:publish` Artisan command: @@ -162,7 +164,8 @@ By default, applications running via Octane generate links prefixed with `http:/ ### Serving Your Application Via Nginx -> {tip} 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 Octane application, check out [Laravel Forge](https://forge.laravel.com). +> **Note** +> 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 Octane application, check out [Laravel Forge](https://forge.laravel.com). In production environments, you should serve your Octane application behind a traditional web server such as a Nginx or Apache. Doing so will allow the web server to serve your static assets such as images and stylesheets, as well as manage your SSL certificate termination. @@ -257,7 +260,7 @@ php artisan octane:start --workers=4 --task-workers=6 ### Specifying The Max Request Count -To help prevent stray memory leaks, Octane can gracefully restart a worker once it has handled a given number of requests. To instruct Octane to do this, you may use the `--max-requests` option: +To help prevent stray memory leaks, Octane gracefully restarts any worker once it has handled 500 requests. To adjust this number, you may use the `--max-requests` option: ```shell php artisan octane:start --max-requests=250 @@ -382,7 +385,8 @@ $service->method($request->input('name')); The global `request` helper will always return the request the application is currently handling and is therefore safe to use within your application. -> {note} It is acceptable to type-hint the `Illuminate\Http\Request` instance on your controller methods and route closures. +> **Warning** +> It is acceptable to type-hint the `Illuminate\Http\Request` instance on your controller methods and route closures. ### Configuration Repository Injection @@ -453,7 +457,8 @@ While building your application, you should take special care to avoid creating ## Concurrent Tasks -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may execute operations concurrently via light-weight background tasks. You may accomplish this using Octane's `concurrently` method. You may combine this method with PHP array destructuring to retrieve the results of each operation: @@ -477,7 +482,8 @@ php artisan octane:start --workers=4 --task-workers=6 ## Ticks & Intervals -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may register "tick" operations that will be executed every specified number of seconds. You may register "tick" callbacks via the `tick` method. The first argument provided to the `tick` method should be a string that represents the name of the ticker. The second argument should be a callable that will be invoked at the specified interval. @@ -499,7 +505,8 @@ Octane::tick('simple-ticker', fn () => ray('Ticking...')) ## The Octane Cache -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may leverage the Octane cache driver, which provides read and write speeds of up to 2 million operations per second. Therefore, this cache driver is an excellent choice for applications that need extreme read / write speeds from their caching layer. @@ -509,7 +516,8 @@ This cache driver is powered by [Swoole tables](https://www.swoole.co.uk/docs/mo Cache::store('octane')->put('framework', 'Laravel', 30); ``` -> {tip} The maximum number of entries allowed in the Octane cache may be defined in your application's `octane` configuration file. +> **Note** +> The maximum number of entries allowed in the Octane cache may be defined in your application's `octane` configuration file. ### Cache Intervals @@ -527,7 +535,8 @@ Cache::store('octane')->interval('random', function () { ## Tables -> {note} This feature requires [Swoole](#swoole). +> **Warning** +> This feature requires [Swoole](#swoole). When using Swoole, you may define and interact with your own arbitrary [Swoole tables](https://www.swoole.co.uk/docs/modules/swoole-table). Swoole tables provide extreme performance throughput and the data in these tables can be accessed by all workers on the server. However, the data within them will be lost when the server is restarted. @@ -555,4 +564,5 @@ Octane::table('example')->set('uuid', [ return Octane::table('example')->get('uuid'); ``` -> {note} The column types supported by Swoole tables are: `string`, `int`, and `float`. +> **Warning** +> The column types supported by Swoole tables are: `string`, `int`, and `float`. diff --git a/packages.md b/packages.md index 6e667aec555..427459334e9 100644 --- a/packages.md +++ b/packages.md @@ -11,6 +11,7 @@ - [Translations](#translations) - [Views](#views) - [View Components](#view-components) + - ["About" Artisan Command](#about-artisan-command) - [Commands](#commands) - [Public Assets](#public-assets) - [Publishing File Groups](#publishing-file-groups) @@ -107,7 +108,8 @@ Now, when users of your package execute Laravel's `vendor:publish` command, your $value = config('courier.option'); -> {note} You should not define closures in your configuration files. They can not be serialized correctly when users execute the `config:cache` Artisan command. +> **Warning** +> 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 @@ -128,7 +130,8 @@ The `mergeConfigFrom` method accepts the path to your package's configuration fi ); } -> {note} This method only merges the first level of the configuration array. If your users partially define a multi-dimensional configuration array, the missing options will not be merged. +> **Warning** +> This method only merges the first level of the configuration array. If your users partially define a multi-dimensional configuration array, the missing options will not be merged. ### Routes @@ -308,6 +311,23 @@ If your package contains anonymous components, they must be placed within a `com ``` + +### "About" Artisan Command + +Laravel's built-in `about` Artisan command provides a synopsis of the application's environment and configuration. Packages may push additional information to this command's output via the `AboutCommand` class. Typically, this information may be added from your package service provider's `boot` method: + + use Illuminate\Foundation\Console\AboutCommand; + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot() + { + AboutCommand::add('My Package', fn () => ['Version' => '1.0.0']); + } + ## Commands diff --git a/pagination.md b/pagination.md index 135c642c1d5..a8c231b217f 100644 --- a/pagination.md +++ b/pagination.md @@ -20,7 +20,7 @@ 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. +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. #### Tailwind JIT @@ -126,7 +126,8 @@ You may create a cursor based paginator instance via the `cursorPaginate` method Once you have retrieved a cursor paginator instance, you may [display the pagination results](#displaying-pagination-results) as you typically would when using the `paginate` and `simplePaginate` methods. For more information on the instance methods offered by the cursor paginator, please consult the [cursor paginator instance method documentation](#cursor-paginator-instance-methods). -> {note} Your query must contain an "order by" clause in order to take advantage of cursor pagination. +> **Warning** +> Your query must contain an "order by" clause in order to take advantage of cursor pagination. #### Cursor vs. Offset Pagination @@ -150,7 +151,8 @@ However, cursor pagination has the following limitations: - Like `simplePaginate`, cursor pagination can only be used to display "Next" and "Previous" links and does not support generating links with page numbers. - It requires that the ordering is based on at least one unique column or a combination of columns that are unique. Columns with `null` values are not supported. -- Query expressions in "order by" clauses are supported only if they are aliased and added to the "select" clause as well. +- Query expressions in "order by" clauses are supported only if they are aliased and added to the "select" clause as well. +- Query expressions with parameters are not supported. ### Manually Creating A Paginator @@ -161,7 +163,8 @@ The `Paginator` and `CursorPaginator` classes do not need to know the total numb In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder, the `CursorPaginator` corresponds to the `cursorPaginate` method, and 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. +> **Warning** +> 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. ### Customizing Pagination URLs @@ -209,7 +212,7 @@ If you need to append a "hash fragment" to URLs generated by the paginator, you When calling the `paginate` method, you will receive an instance of `Illuminate\Pagination\LengthAwarePaginator`, while calling the `simplePaginate` method returns an instance of `Illuminate\Pagination\Paginator`. And, finally, calling the `cursorPaginate` method returns an instance of `Illuminate\Pagination\CursorPaginator`. -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): +These objects provide several methods that describe the result set. In addition to these helper 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): ```blade
diff --git a/passport.md b/passport.md index aaa7c86ca6e..b068f3b8d23 100644 --- a/passport.md +++ b/passport.md @@ -48,7 +48,8 @@ [Laravel Passport](https://github.com/laravel/passport) provides a full OAuth2 server implementation for your Laravel application in a matter of minutes. Passport is built on top of the [League OAuth2 server](https://github.com/thephpleague/oauth2-server) that is maintained by Andy Millington and Simon Hamp. -> {note} This documentation assumes you are already familiar with OAuth2. If you do not know anything about OAuth2, consider familiarizing yourself with the general [terminology](https://oauth2.thephpleague.com/terminology/) and features of OAuth2 before continuing. +> **Warning** +> This documentation assumes you are already familiar with OAuth2. If you do not know anything about OAuth2, consider familiarizing yourself with the general [terminology](https://oauth2.thephpleague.com/terminology/) and features of OAuth2 before continuing. ### Passport Or Sanctum? @@ -78,7 +79,8 @@ Next, you should execute the `passport:install` Artisan command. This command wi php artisan passport:install ``` -> {tip} If you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers, please install Passport using [the `uuids` option](#client-uuids). +> **Note** +> If you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers, please install Passport using [the `uuids` option](#client-uuids). After running the `passport:install` command, add the `Laravel\Passport\HasApiTokens` trait to your `App\Models\User` model. This trait will provide a few helper methods to your model which allow you to inspect the authenticated user's token and scopes. If your model is already using the `Laravel\Sanctum\HasApiTokens` trait, you may remove that trait: @@ -96,43 +98,7 @@ After running the `passport:install` command, add the `Laravel\Passport\HasApiTo use HasApiTokens, HasFactory, Notifiable; } -Next, you should call the `Passport::routes` method within the `boot` method of your `App\Providers\AuthServiceProvider`. This method will register the routes necessary to issue access tokens and revoke access tokens, clients, and personal access tokens: - - 'App\Policies\ModelPolicy', - ]; - - /** - * Register any authentication / authorization services. - * - * @return void - */ - public function boot() - { - $this->registerPolicies(); - - if (! $this->app->routesAreCached()) { - Passport::routes(); - } - } - } - -Finally, in your application's `config/auth.php` configuration file, you should set the `driver` option of the `api` authentication guard to `passport`. This will instruct your application to use Passport's `TokenGuard` when authenticating incoming API requests: +Finally, in your application's `config/auth.php` configuration file, you should define an `api` authentication guard and set the `driver` option to `passport`. This will instruct your application to use Passport's `TokenGuard` when authenticating incoming API requests: 'guards' => [ 'web' => [ @@ -175,8 +141,6 @@ If necessary, you may define the path where Passport's keys should be loaded fro { $this->registerPolicies(); - Passport::routes(); - Passport::loadKeysFrom(__DIR__.'/../secrets/oauth'); } @@ -243,14 +207,13 @@ By default, Passport issues long-lived access tokens that expire after one year. { $this->registerPolicies(); - Passport::routes(); - Passport::tokensExpireIn(now()->addDays(15)); Passport::refreshTokensExpireIn(now()->addDays(30)); Passport::personalAccessTokensExpireIn(now()->addMonths(6)); } -> {note} The `expires_at` columns on Passport's database tables are read-only and for display purposes only. When issuing tokens, Passport stores the expiration information within the signed and encrypted tokens. If you need to invalidate a token you should [revoke it](#revoking-tokens). +> **Warning** +> The `expires_at` columns on Passport's database tables are read-only and for display purposes only. When issuing tokens, Passport stores the expiration information within the signed and encrypted tokens. If you need to invalidate a token you should [revoke it](#revoking-tokens). ### Overriding Default Models @@ -280,8 +243,6 @@ After defining your model, you may instruct Passport to use your custom model vi { $this->registerPolicies(); - Passport::routes(); - Passport::useTokenModel(Token::class); Passport::useClientModel(Client::class); Passport::useAuthCodeModel(AuthCode::class); @@ -415,7 +376,8 @@ Once a client has been created, developers may use their client ID and secret to return redirect('http://passport-app.test/oauth/authorize?'.$query); }); -> {tip} Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route. +> **Note** +> Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. #### Approving The Request @@ -478,7 +440,8 @@ If the user approves the authorization request, they will be redirected back to This `/oauth/token` route will return a JSON response containing `access_token`, `refresh_token`, and `expires_in` attributes. The `expires_in` attribute contains the number of seconds until the access token expires. -> {tip} Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by the `Passport::routes` method. There is no need to manually define this route. +> **Note** +> Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by Passport. There is no need to manually define this route. #### JSON API @@ -667,7 +630,8 @@ If the state parameter matches, the consumer should issue a `POST` request to yo ## Password Grant Tokens -> {note} We no longer recommend using password grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). +> **Warning** +> We no longer recommend using password grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). The OAuth2 password grant allows your other first-party clients, such as a mobile application, to obtain an access token using an email address / username and password. This allows you to issue access tokens securely to your first-party clients without requiring your users to go through the entire OAuth2 authorization code redirect flow. @@ -683,7 +647,7 @@ php artisan passport:client --password ### Requesting Tokens -Once you have created a password grant client, you may request an access token by issuing a `POST` request to the `/oauth/token` route with the user's email address and password. Remember, this route is already registered by the `Passport::routes` method so there is no need to define it manually. If the request is successful, you will receive an `access_token` and `refresh_token` in the JSON response from the server: +Once you have created a password grant client, you may request an access token by issuing a `POST` request to the `/oauth/token` route with the user's email address and password. Remember, this route is already registered by Passport so there is no need to define it manually. If the request is successful, you will receive an `access_token` and `refresh_token` in the JSON response from the server: use Illuminate\Support\Facades\Http; @@ -698,7 +662,8 @@ Once you have created a password grant client, you may request an access token b return $response->json(); -> {tip} Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed. +> **Note** +> Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed. ### Requesting All Scopes @@ -783,7 +748,8 @@ When authenticating using the password grant, Passport will use the `password` a ## Implicit Grant Tokens -> {note} We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). +> **Warning** +> We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). The implicit grant is similar to the authorization code grant; however, the token is returned to the client without exchanging an authorization code. This grant is most commonly used for JavaScript or mobile applications where the client credentials can't be securely stored. To enable the grant, call the `enableImplicitGrant` method in the `boot` method of your application's `App\Providers\AuthServiceProvider` class: @@ -796,8 +762,6 @@ The implicit grant is similar to the authorization code grant; however, the toke { $this->registerPolicies(); - Passport::routes(); - Passport::enableImplicitGrant(); } @@ -819,7 +783,8 @@ Once the grant has been enabled, developers may use their client ID to request a return redirect('http://passport-app.test/oauth/authorize?'.$query); }); -> {tip} Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route. +> **Note** +> Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. ## Client Credentials Grant Tokens @@ -873,7 +838,8 @@ To retrieve a token using this grant type, make a request to the `oauth/token` e Sometimes, your users may want to issue access tokens to themselves without going through the typical authorization code redirect flow. Allowing users to issue tokens to themselves via your application's UI can be useful for allowing users to experiment with your API or may serve as a simpler approach to issuing access tokens in general. -> {tip} If your application is primarily using Passport to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. +> **Note** +> If your application is primarily using Passport to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. ### Creating A Personal Access Client @@ -978,7 +944,8 @@ Passport includes an [authentication guard](/docs/{{version}}/authentication#add // })->middleware('auth:api'); -> {note} If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. +> **Warning** +> If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. #### Multiple Authentication Guards @@ -1001,7 +968,8 @@ The following route will utilize the `api-customers` guard, which uses the `cust // })->middleware('auth:api-customers'); -> {tip} For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). +> **Note** +> For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). ### Passing The Access Token @@ -1036,8 +1004,6 @@ You may define your API's scopes using the `Passport::tokensCan` method in the ` { $this->registerPolicies(); - Passport::routes(); - Passport::tokensCan([ 'place-orders' => 'Place orders', 'check-status' => 'Check order status', @@ -1159,7 +1125,8 @@ Typically, if you want to consume your API from your JavaScript application, you \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, ], -> {note} You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. +> **Warning** +> You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack. This middleware will attach a `laravel_token` cookie to your outgoing responses. This cookie contains an encrypted JWT that Passport will use to authenticate API requests from your JavaScript application. The JWT has a lifetime equal to your `session.lifetime` configuration value. Now, since the browser will automatically send the cookie with all subsequent requests, you may make requests to your application's API without explicitly passing an access token: @@ -1182,8 +1149,6 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo { $this->registerPolicies(); - Passport::routes(); - Passport::cookie('custom_name'); } @@ -1192,7 +1157,8 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo When using this method of authentication, you will need to ensure a valid CSRF token header is included in your requests. The default Laravel JavaScript scaffolding includes an Axios instance, which will automatically use the encrypted `XSRF-TOKEN` cookie value to send an `X-XSRF-TOKEN` header on same-origin requests. -> {tip} If you choose to send the `X-CSRF-TOKEN` header instead of `X-XSRF-TOKEN`, you will need to use the unencrypted token provided by `csrf_token()`. +> **Note** +> If you choose to send the `X-CSRF-TOKEN` header instead of `X-XSRF-TOKEN`, you will need to use the unencrypted token provided by `csrf_token()`. ## Events diff --git a/passwords.md b/passwords.md index 1aa2e2ea0c0..fb1efd6cc17 100644 --- a/passwords.md +++ b/passwords.md @@ -15,7 +15,8 @@ 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 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. +> **Note** +> 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. ### Model Preparation @@ -87,7 +88,8 @@ The `sendResetLink` method returns a "status" slug. This status may be translate You may be 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). +> **Note** +> 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/pint.md b/pint.md new file mode 100644 index 00000000000..00fdb18fb6f --- /dev/null +++ b/pint.md @@ -0,0 +1,138 @@ +# Laravel Pint + +- [Introduction](#introduction) +- [Installation](#installation) +- [Running Pint](#running-pint) +- [Configuring Pint](#configuring-pint) + - [Presets](#presets) + - [Rules](#rules) + - [Excluding Files / Folders](#excluding-files-or-folders) + + +## Introduction + +[Laravel Pint](https://github.com/laravel/pint) is an opinionated PHP code style fixer for minimalists. Pint is built on top of PHP-CS-Fixer and makes it simple to ensure that your code style stays clean and consistent. + +Pint is automatically installed with all new Laravel applications so you may start using it immediately. By default, Pint does not require any configuration and will fix code style issues in your code by following the opinionated coding style of Laravel. + + +## Installation + +Pint is included in recent releases of the Laravel framework, so installation is typically unnecessary. However, for older applications, you may install Laravel Pint via Composer: + +```shell +composer require laravel/pint --dev +``` + + +## Running Pint + +You can instruct Pint to fix code style issues by invoking the `pint` binary that is available in your project's `vendor/bin` directory: + +```shell +./vendor/bin/pint +``` + +Pint will display a thorough list of all of the files that it updates. You can view even more detail about Pint's changes by providing the `-v` option when invoking Pint: + +```shell +./vendor/bin/pint -v +``` + +If you would like Pint to simply inspect your code for style errors without actually changing the files, you may use the `--test` option: + +```shell +./vendor/bin/pint --test +``` + + +## Configuring Pint + +As previously mentioned, Pint does not require any configuration. However, if you wish to customize the presets, rules, or inspected folders, you may do so by creating a `pint.json` file in your project's root directory: + +```json +{ + "preset": "laravel" +} +``` + +In addition, if you wish to use a `pint.json` from a specific directory, you may provide the `--config` option when invoking Pint: + +```shell +pint --config vendor/my-company/coding-style/pint.json +``` + + +### Presets + +Presets defines a set of rules that can be used to fix code style issues in your code. By default, Pint uses the `laravel` preset, which fixes issues by following the opinionated coding style of Laravel. However, you may specify a different preset by providing the `--preset` option to Pint: + +```shell +pint --preset psr12 +``` + +If you wish, you may also set the preset in your project's `pint.json` file: + +```json +{ + "preset": "psr12" +} +``` + +Pint's currently supported presets are: `laravel`, `psr12`, and `symfony`. + + +### Rules + +Rules are style guidelines that Pint will use to fix code style issues in your code. As mentioned above, presets are predefined groups of rules that should be perfect for most PHP projects, so you typically will not need to worry about the individual rules they contain. + +However, if you wish, you may enable or disable specific rules in your `pint.json` file: + +```json +{ + "preset": "laravel", + "rules": { + "simplified_null_return": true, + "braces": false, + "new_with_braces": { + "anonymous_class": false, + "named_class": false + } + } +} +``` + +Pint is built on top of [PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer). Therefore, you may use any of its rules to fix code style issues in your project: [PHP-CS-Fixer Configurator](https://mlocati.github.io/php-cs-fixer-configurator). + + +### Excluding Files / Folders + +By default, Pint will inspect all `.php` files in your project except those in the `vendor` directory. If you wish to exclude more folders, you may do so using the `exclude` configuration option: + +```json +{ + "exclude": [ + "my-specific/folder" + ] +} +``` + +If you wish to exclude all files that contain a given name pattern, you may do so using the `notName` configuration option: + +```json +{ + "notName": [ + "*-my-file.php" + ] +} +``` + +If you would like to exclude a file by providing an exact path to the file, you may do so using the `notPath` configuration option: + +```json +{ + "notPath": [ + "path/to/excluded-file.php" + ] +} +``` diff --git a/providers.md b/providers.md index 8fc544bfd09..69aa92f8ffa 100644 --- a/providers.md +++ b/providers.md @@ -18,7 +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). +> **Note** +> 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 diff --git a/queries.md b/queries.md index 7f3970e13db..8e9e7917824 100644 --- a/queries.md +++ b/queries.md @@ -41,7 +41,8 @@ Laravel's database query builder provides a convenient, fluent interface to crea 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. +> **Warning** +> 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. ## Running Database Queries @@ -83,7 +84,8 @@ The `get` method returns an `Illuminate\Support\Collection` instance containing echo $user->name; } -> {tip} Laravel collections provide a variety of extremely powerful methods for mapping and reducing data. For more information on Laravel collections, check out the [collection documentation](/docs/{{version}}/collections). +> **Note** +> Laravel collections provide a variety of extremely powerful methods for mapping and reducing data. For more information on Laravel collections, check out the [collection documentation](/docs/{{version}}/collections). #### Retrieving A Single Row / Column From A Table @@ -155,7 +157,8 @@ If you are updating database records while chunking results, your chunk results } }); -> {note} When updating or deleting records inside the chunk callback, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the chunked results. +> **Warning** +> When updating or deleting records inside the chunk callback, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the chunked results. ### Streaming Results Lazily @@ -181,7 +184,8 @@ DB::table('users')->where('active', false) }); ``` -> {note} When updating or deleting records while iterating over them, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the results. +> **Warning** +> When updating or deleting records while iterating over them, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the results. ### Aggregates @@ -248,7 +252,8 @@ Sometimes you may need to insert an arbitrary string into a query. To create a r ->groupBy('status') ->get(); -> {note} Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities. +> **Warning** +> Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities. ### Raw Methods @@ -258,7 +263,7 @@ Instead of using the `DB::raw` method, you may also use the following methods to #### `selectRaw` -The `selectRaw` method can be used in place of `addSelect(DB::raw(...))`. This method accepts an optional array of bindings as its second argument: +The `selectRaw` method can be used in place of `addSelect(DB::raw(/* ... */))`. This method accepts an optional array of bindings as its second argument: $orders = DB::table('orders') ->selectRaw('price * ? as price_with_tax', [1.0825]) @@ -348,7 +353,7 @@ You may also specify more advanced join clauses. To get started, pass a closure DB::table('users') ->join('contacts', function ($join) { - $join->on('users.id', '=', 'contacts.user_id')->orOn(...); + $join->on('users.id', '=', 'contacts.user_id')->orOn(/* ... */); }) ->get(); @@ -433,7 +438,8 @@ You may also pass an array of conditions to the `where` function. Each element o ['subscribed', '<>', '1'], ])->get(); -> {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. +> **Warning** +> 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. ### Or Where Clauses @@ -461,7 +467,8 @@ The example above will produce the following 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. +> **Warning** +> You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. ### Where Not Clauses @@ -484,7 +491,7 @@ Laravel also supports querying JSON column types on databases that provide suppo ->where('preferences->dining->meal', 'salad') ->get(); -You may use `whereJsonContains` to query JSON arrays. This feature is not supported by the SQLite database: +You may use `whereJsonContains` to query JSON arrays. This feature is not supported by SQLite database versions less than 3.38.0: $users = DB::table('users') ->whereJsonContains('options->languages', 'en') @@ -539,7 +546,8 @@ The `whereNotIn` method verifies that the given column's value is not contained ->whereNotIn('id', [1, 2, 3]) ->get(); -> {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. +> **Warning** +> 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** @@ -628,7 +636,8 @@ As you can see, passing a closure into the `where` method instructs the query bu 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. +> **Warning** +> You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied. ### Advanced Where Clauses @@ -683,7 +692,8 @@ Or, you may need to construct a "where" clause that compares a column to the res ### Full Text Where Clauses -> {note} Full text where clauses are currently supported by MySQL and PostgreSQL. +> **Warning** +> Full text where clauses are currently supported by MySQL and PostgreSQL. The `whereFullText` and `orWhereFullText` methods may be used to add full text "where" clauses to a query for columns that have [full text indexes](/docs/{{version}}/migrations#available-index-types). These methods will be transformed into the appropriate SQL for the underlying database system by Laravel. For example, a `MATCH AGAINST` clause will be generated for applications utilizing MySQL: @@ -861,21 +871,27 @@ If the table has an auto-incrementing id, use the `insertGetId` method to insert ['email' => 'john@example.com', 'votes' => 0] ); -> {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. +> **Warning** +> 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']); + 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. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. +> **Warning** +> All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index. In addition, the MySQL database driver ignores the second argument of the `upsert` method and always uses the "primary" and "unique" indexes of the table to detect existing records. ## Update Statements diff --git a/queues.md b/queues.md index 5dc0e8f3eb5..32db7ea9d27 100644 --- a/queues.md +++ b/queues.md @@ -55,7 +55,8 @@ Laravel queues provide a unified queueing API across a variety of different queu 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. -> {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. +> **Note** +> 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. ### Connections Vs. Queues @@ -126,7 +127,8 @@ Adjusting this value based on your queue load can be more efficient than continu 'block_for' => 5, ], -> {note} Setting `block_for` to `0` will cause queue workers to block indefinitely until a job is available. This will also prevent signals such as `SIGTERM` from being handled until the next job has been processed. +> **Warning** +> Setting `block_for` to `0` will cause queue workers to block indefinitely until a job is available. This will also prevent signals such as `SIGTERM` from being handled until the next job has been processed. #### Other Driver Prerequisites @@ -155,7 +157,8 @@ php artisan make:job ProcessPodcast The generated class will implement the `Illuminate\Contracts\Queue\ShouldQueue` interface, indicating to Laravel that the job should be pushed onto the queue to run asynchronously. -> {tip} Job stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). +> **Note** +> Job stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). ### Class Structure @@ -183,7 +186,7 @@ Job classes are very simple, normally containing only a `handle` method that is * * @var \App\Models\Podcast */ - protected $podcast; + public $podcast; /** * Create a new job instance. @@ -226,7 +229,8 @@ If you would like to take total control over how the container injects dependenc return $job->handle($app->make(AudioProcessor::class)); }); -> {note} Binary data, such as raw image contents, should be passed through the `base64_encode` function before being passed to a queued job. Otherwise, the job may not properly serialize to JSON when being placed on the queue. +> **Warning** +> Binary data, such as raw image contents, should be passed through the `base64_encode` function before being passed to a queued job. Otherwise, the job may not properly serialize to JSON when being placed on the queue. #### Queued Relationships @@ -249,7 +253,8 @@ Furthermore, when a job is deserialized and model relationships are re-retrieved ### Unique Jobs -> {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. In addition, unique job constraints do not apply to jobs within batches. +> **Warning** +> 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. In addition, unique job constraints do not apply to jobs within batches. 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: @@ -302,7 +307,8 @@ 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. -> {note} If your application dispatches jobs from multiple web servers or containers, you should ensure that all of your servers are communicating with the same central cache server so that Laravel can accurately determine if a job is unique. +> **Warning** +> If your application dispatches jobs from multiple web servers or containers, you should ensure that all of your servers are communicating with the same central cache server so that Laravel can accurately determine if a job is unique. #### Keeping Jobs Unique Until Processing Begins @@ -342,7 +348,8 @@ Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts t } } -> {tip} If you only need to limit the concurrent processing of a job, use the [`WithoutOverlapping`](/docs/{{version}}/queues#preventing-job-overlaps) job middleware instead. +> **Note** +> If you only need to limit the concurrent processing of a job, use the [`WithoutOverlapping`](/docs/{{version}}/queues#preventing-job-overlaps) job middleware instead. ## Job Middleware @@ -420,7 +427,8 @@ After creating job middleware, they may be attached to a job by returning them f return [new RateLimited]; } -> {tip} Job middleware can also be assigned to queueable event listeners, mailables, and notifications. +> **Note** +> Job middleware can also be assigned to queueable event listeners, mailables, and notifications. ### Rate Limiting @@ -478,7 +486,8 @@ If you do not want a job to be retried when it is rate limited, you may use the return [(new RateLimited('backups'))->dontRelease()]; } -> {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. +> **Note** +> 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. ### Preventing Job Overlaps @@ -535,7 +544,8 @@ The `WithoutOverlapping` middleware is powered by Laravel's atomic lock feature. return [(new WithoutOverlapping($this->order->id))->expireAfter(180)]; } -> {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. +> **Warning** +> 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. ### Throttling Exceptions @@ -596,7 +606,8 @@ Internally, this middleware uses Laravel's cache system to implement rate limiti return [(new ThrottlesExceptions(10, 10))->by('key')]; } -> {tip} If you are using Redis, you may use the `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic exception throttling middleware. +> **Note** +> If you are using Redis, you may use the `Illuminate\Queue\Middleware\ThrottlesExceptionsWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic exception throttling middleware. ## Dispatching Jobs @@ -622,7 +633,7 @@ Once you have written your job class, you may dispatch it using the `dispatch` m */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // ... @@ -636,6 +647,8 @@ If you would like to conditionally dispatch a job, you may use the `dispatchIf` ProcessPodcast::dispatchUnless($accountSuspended, $podcast); +In new Laravel applications, the `sync` driver is the default queue driver. This driver executes jobs synchronously in the foreground of the current request, which is often convenient during local development. If you would like to actually begin queueing jobs for background processing, you may specify a different queue driver within your application's `config/queue.php` configuration file. + ### Delayed Dispatching @@ -660,7 +673,7 @@ If you would like to specify that a job should not be immediately available for */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // ... @@ -669,7 +682,8 @@ If you would like to specify that a job should not be immediately available for } } -> {note} The Amazon SQS queue service has a maximum delay time of 15 minutes. +> **Warning** +> The Amazon SQS queue service has a maximum delay time of 15 minutes. #### Dispatching After The Response Is Sent To Browser @@ -713,7 +727,7 @@ If you would like to dispatch a job immediately (synchronously), you may use the */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // Create podcast... @@ -738,7 +752,8 @@ When the `after_commit` option is `true`, you may dispatch jobs within database If a transaction is rolled back due to an exception that occurs during the transaction, the jobs that were dispatched during that transaction will be discarded. -> {tip} Setting the `after_commit` configuration option to `true` will also cause any queued event listeners, mailables, notifications, and broadcast events to be dispatched after all open database transactions have been committed. +> **Note** +> Setting the `after_commit` configuration option to `true` will also cause any queued event listeners, mailables, notifications, and broadcast events to be dispatched after all open database transactions have been committed. #### Specifying Commit Dispatch Behavior Inline @@ -775,11 +790,12 @@ In addition to chaining job class instances, you may also chain closures: new ProcessPodcast, new OptimizePodcast, function () { - Podcast::update(...); + Podcast::update(/* ... */); }, ])->dispatch(); -> {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. +> **Warning** +> 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. #### Chain Connection & Queue @@ -835,7 +851,7 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // Create podcast... @@ -894,7 +910,7 @@ If your application interacts with multiple queue connections, you may specify w */ public function store(Request $request) { - $podcast = Podcast::create(...); + $podcast = Podcast::create(/* ... */); // Create podcast... @@ -982,7 +998,8 @@ As an alternative to defining how many times a job may be attempted before it fa return now()->addMinutes(10); } -> {tip} You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners). +> **Note** +> You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners). #### Max Exceptions @@ -1032,9 +1049,10 @@ In this example, the job is released for ten seconds if the application is unabl #### Timeout -> {note} The `pcntl` PHP extension must be installed in order to specify job timeouts. +> **Warning** +> The `pcntl` PHP extension must be installed in order to specify job timeouts. -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). +Often, you know roughly how long you expect your queued jobs to take. For this reason, Laravel allows you to specify a "timeout" value. By default, the timeout value is 60 seconds. 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: @@ -1123,7 +1141,8 @@ If you would like to mark your job as failed because of an exception that you ha $this->fail($exception); -> {tip} For more information on failed jobs, check out the [documentation on dealing with job failures](#dealing-with-failed-jobs). +> **Note** +> For more information on failed jobs, check out the [documentation on dealing with job failures](#dealing-with-failed-jobs). ## Job Batching @@ -1201,7 +1220,8 @@ To dispatch a batch of jobs, you should use the `batch` method of the `Bus` faca 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. -> {note} Since batch callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within the callbacks. +> **Warning** +> Since batch callbacks are serialized and executed at a later time by the Laravel queue, you should not use the `$this` variable within the callbacks. #### Naming Batches @@ -1282,7 +1302,8 @@ In this example, we will use the `LoadImportBatch` job to hydrate the batch with })); } -> {note} You may only add jobs to a batch from within a job that belongs to the same batch. +> **Warning** +> You may only add jobs to a batch from within a job that belongs to the same batch. ### Inspecting Batches @@ -1443,9 +1464,10 @@ Laravel includes an Artisan command that will start a queue worker and process n php artisan queue:work ``` -> {tip} To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. +> **Note** +> To keep the `queue:work` process running permanently in the background, you should use a process monitor such as [Supervisor](#supervisor-configuration) to ensure that the queue worker does not stop running. -Remember, queue workers, are long-lived processes and 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. +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 significantly less efficient than the `queue:work` command: @@ -1545,7 +1567,8 @@ php artisan queue:restart 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 that a cache driver is properly configured for your application before using this feature. +> **Note** +> 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. ### Job Expirations & Timeouts @@ -1555,12 +1578,13 @@ This command will instruct all queue workers to gracefully exit after they finis 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. +> **Warning** +> The only queue connection which does not contain a `retry_after` value is Amazon SQS. SQS will retry the job based on the [Default Visibility Timeout](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html) which is managed within the AWS console. #### Worker Timeouts -The `queue:work` Artisan command exposes a `--timeout` option. 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 `queue:work` Artisan command exposes a `--timeout` option. By default, the `--timeout` value is 60 seconds. 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): ```shell php artisan queue:work --timeout=60 @@ -1568,7 +1592,8 @@ 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 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. +> **Warning** +> 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. ## Supervisor Configuration @@ -1586,7 +1611,8 @@ Supervisor is a process monitor for the Linux operating system, and will automat sudo apt-get install supervisor ``` -> {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. +> **Note** +> 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. #### Configuring Supervisor @@ -1610,7 +1636,8 @@ stopwaitsecs=3600 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. +> **Warning** +> 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. #### Starting Supervisor @@ -1711,7 +1738,7 @@ When a particular job fails, you may want to send an alert to your users or reve * * @var \App\Podcast */ - protected $podcast; + public $podcast; /** * Create a new job instance. @@ -1747,7 +1774,8 @@ When a particular job fails, you may want to send an alert to your users or reve } } -> {note} A new instance of the job is instantiated before invoking the `failed` method; therefore, any class property modifications that may have occurred within the `handle` method will be lost. +> **Warning** +> A new instance of the job is instantiated before invoking the `failed` method; therefore, any class property modifications that may have occurred within the `handle` method will be lost. ### Retrying Failed Jobs @@ -1788,7 +1816,8 @@ If you would like to delete a failed job, you may use the `queue:forget` command php artisan queue:forget 91401d2c-0784-4f43-824c-34f94a33c24d ``` -> {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. +> **Note** +> 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 from the `failed_jobs` table, you may use the `queue:flush` command: @@ -1902,7 +1931,8 @@ If you would like to register an event listener that will be invoked when a job ## Clearing Jobs From Queues -> {tip} When using [Horizon](/docs/{{version}}/horizon), you should use the `horizon:clear` command to clear jobs from the queue instead of the `queue:clear` command. +> **Note** +> When using [Horizon](/docs/{{version}}/horizon), you should use the `horizon:clear` command to clear jobs from the queue instead of the `queue:clear` command. If you would like to delete all jobs from the default queue of the default connection, you may do so using the `queue:clear` Artisan command: @@ -1916,7 +1946,8 @@ You may also provide the `connection` argument and `queue` option to delete jobs php artisan queue:clear redis --queue=emails ``` -> {note} Clearing jobs from queues is only available for the SQS, Redis, and database queue drivers. In addition, the SQS message deletion process takes up to 60 seconds, so jobs sent to the SQS queue up to 60 seconds after you clear the queue might also be deleted. +> **Warning** +> Clearing jobs from queues is only available for the SQS, Redis, and database queue drivers. In addition, the SQS message deletion process takes up to 60 seconds, so jobs sent to the SQS queue up to 60 seconds after you clear the queue might also be deleted. ## Monitoring Your Queues diff --git a/rate-limiting.md b/rate-limiting.md index cbffa7486a7..6800b986016 100644 --- a/rate-limiting.md +++ b/rate-limiting.md @@ -11,7 +11,8 @@ Laravel includes a simple to use rate limiting abstraction which, in conjunction with your application's [cache](cache), provides an easy way to limit any action during a specified window of time. -> {tip} If you are interested in rate limiting incoming HTTP requests, please consult the [rate limiter middleware documentation](routing#rate-limiting). +> **Note** +> If you are interested in rate limiting incoming HTTP requests, please consult the [rate limiter middleware documentation](routing#rate-limiting). ### Cache Configuration diff --git a/redis.md b/redis.md index ba670195757..7f46e144571 100644 --- a/redis.md +++ b/redis.md @@ -183,7 +183,7 @@ In addition to the default `scheme`, `host`, `port`, `database`, and `password` #### phpredis Serialization & Compression -The phpredis extension may also be configured to use a variety serialization and compression algorithms. These algorithms can be configured via the `options` array of your Redis configuration: +The phpredis extension may also be configured to use a variety of serialization and compression algorithms. These algorithms can be configured via the `options` array of your Redis configuration: 'redis' => [ @@ -264,7 +264,8 @@ The `Redis` facade's `transaction` method provides a convenient wrapper around R $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. +> **Warning** +> 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 @@ -284,7 +285,8 @@ In this example, we will increment a counter, inspect its new value, and increme 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. +> **Warning** +> Please consult the [Redis documentation](https://redis.io/commands/eval) for more information on Redis scripting. ### Pipelining Commands diff --git a/releases.md b/releases.md index 69a2d4d7186..2b6aa90422a 100644 --- a/releases.md +++ b/releases.md @@ -19,7 +19,7 @@ When referencing the Laravel framework or its components from your application o ## Support Policy -For all Laravel releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, including Lumen, only the latest release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction). +For all Laravel releases, bug fixes are provided for 18 months and security fixes are provided for 2 years. For all additional libraries, including Lumen, only the latest major release receives bug fixes. In addition, please review the database versions [supported by Laravel](/docs/{{version}}/database#introduction). | Version | PHP (*) | Release | Bug Fixes Until | Security Fixes Until | | --- | --- | --- | --- | --- | @@ -131,7 +131,8 @@ public function address(): Attribute ### Enum Eloquent Attribute Casting -> {note} Enum casting is only available for PHP 8.1+. +> **Warning** +> Enum casting is only available for PHP 8.1+. _Enum casting was contributed by [Mohamed Said](https://github.com/themsaid)_. diff --git a/requests.md b/requests.md index 4e2daa8675d..003f04dbdcf 100644 --- a/requests.md +++ b/requests.md @@ -3,7 +3,7 @@ - [Introduction](#introduction) - [Interacting With The Request](#interacting-with-the-request) - [Accessing The Request](#accessing-the-request) - - [Request Path & Method](#request-path-and-method) + - [Request Path, Host, & Method](#request-path-and-method) - [Request Headers](#request-headers) - [Request IP Address](#request-ip-address) - [Content Negotiation](#content-negotiation) @@ -97,7 +97,7 @@ You may still type-hint the `Illuminate\Http\Request` and access your `id` route } -### Request Path & Method +### Request Path, Host, & Method 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. @@ -136,6 +136,15 @@ If you would like to append query string data to the current URL, you may call t $request->fullUrlWithQuery(['type' => 'phone']); + +#### Retrieving The Request Host + +You may retrieve the "host" of the incoming request via the `host`, `httpHost`, and `schemeAndHttpHost` methods: + + $request->host(); + $request->httpHost(); + $request->schemeAndHttpHost(); + #### Retrieving The Request Method @@ -214,7 +223,8 @@ Once you have installed these libraries, you may obtain a PSR-7 request by type- // }); -> {tip} If you return a PSR-7 response instance from a route or controller, it will automatically be converted back to a Laravel response instance and be displayed by the framework. +> **Note** +> If you return a PSR-7 response instance from a route or controller, it will automatically be converted back to a Laravel response instance and be displayed by the framework. ## Input @@ -278,7 +288,7 @@ You may call the `query` method without any arguments in order to retrieve all o #### 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 retrieve values that are nested within 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 / objects: $name = $request->input('user.name'); @@ -309,6 +319,15 @@ The second and third arguments accepted by the `date` method may be used to spec If the input value is present but has an invalid format, an `InvalidArgumentException` will be thrown; therefore, it is recommended that you validate the input before invoking the `date` method. + +#### Retrieving Enum Input Values + +Input values that correspond to [PHP enums](https://www.php.net/manual/en/language.types.enumerations.php) may also be retrieved from the request. If the request does not contain an input value with the given name or the enum does not have a backing value that matches the input value, `null` will be returned. The `enum` method accepts the name of the input value and the enum class as its first and second arguments: + + use App\Enums\Status; + + $status = $request->enum('status', Status::class); + #### Retrieving Input Via Dynamic Properties @@ -331,7 +350,8 @@ If you need to retrieve a subset of the input data, you may use the `only` and ` $input = $request->except('credit_card'); -> {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. +> **Warning** +> 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 Input Is Present @@ -368,13 +388,13 @@ The `hasAny` method returns `true` if any of the specified values are present: // } -If you would like to determine if a value is present on the request and is not empty, you may use the `filled` method: +If you would like to determine if a value is present on the request and is not an empty string, you may use the `filled` method: if ($request->filled('name')) { // } -The `whenFilled` method will execute the given closure 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 an empty string: $request->whenFilled('name', function ($input) { // @@ -549,7 +569,8 @@ If you do not want a filename to be automatically generated, you may use the `st $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). +> **Note** +> For more information about file storage in Laravel, check out the complete [file storage documentation](/docs/{{version}}/filesystem). ## Configuring Trusted Proxies @@ -585,7 +606,8 @@ To solve this, you may use the `App\Http\Middleware\TrustProxies` middleware tha protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO; } -> {tip} If you are using AWS Elastic Load Balancing, your `$headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `$headers` property, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/current/deployment/proxies.html). +> **Note** +> If you are using AWS Elastic Load Balancing, your `$headers` value should be `Request::HEADER_X_FORWARDED_AWS_ELB`. For more information on the constants that may be used in the `$headers` property, check out Symfony's documentation on [trusting proxies](https://symfony.com/doc/current/deployment/proxies.html). #### Trusting All Proxies diff --git a/responses.md b/responses.md index b74105f0f79..06b232abd95 100644 --- a/responses.md +++ b/responses.md @@ -34,7 +34,8 @@ In addition to returning strings from your routes and controllers, you may also return [1, 2, 3]; }); -> {tip} Did you know you can also return [Eloquent collections](/docs/{{version}}/eloquent-collections) from your routes or controllers? They will automatically be converted to JSON. Give it a shot! +> **Note** +> Did you know you can also return [Eloquent collections](/docs/{{version}}/eloquent-collections) from your routes or controllers? They will automatically be converted to JSON. Give it a shot! #### Response Objects @@ -288,7 +289,8 @@ The `download` method may be used to generate a response that forces the user's return response()->download($pathToFile, $name, $headers); -> {note} Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII filename. +> **Warning** +> Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII filename. #### Streamed Downloads diff --git a/routing.md b/routing.md index f3f65062200..486819e923b 100644 --- a/routing.md +++ b/routing.md @@ -74,7 +74,8 @@ Sometimes you may need to register a route that responds to multiple HTTP verbs. // }); -> {tip} When defining multiple routes that share the same URI, routes using the `get`, `post`, `put`, `patch`, `delete`, and `options` methods should be defined before routes using the `any`, `match`, and `redirect` methods. This ensures the incoming request is matched with the correct route. +> **Note** +> When defining multiple routes that share the same URI, routes using the `get`, `post`, `put`, `patch`, `delete`, and `options` methods should be defined before routes using the `any`, `match`, and `redirect` methods. This ensures the incoming request is matched with the correct route. #### Dependency Injection @@ -112,7 +113,8 @@ Or, you may use the `Route::permanentRedirect` method to return a `301` status c Route::permanentRedirect('/here', '/there'); -> {note} When using route parameters in redirect routes, the following parameters are reserved by Laravel and cannot be used: `destination` and `status`. +> **Warning** +> When using route parameters in redirect routes, the following parameters are reserved by Laravel and cannot be used: `destination` and `status`. ### View Routes @@ -123,7 +125,8 @@ If your route only needs to return a [view](/docs/{{version}}/views), you may us Route::view('/welcome', 'welcome', ['name' => 'Taylor']); -> {note} When using route parameters in view routes, the following parameters are reserved by Laravel and cannot be used: `view`, `data`, `status`, and `headers`. +> **Warning** +> When using route parameters in view routes, the following parameters are reserved by Laravel and cannot be used: `view`, `data`, `status`, and `headers`. ### The Route List @@ -140,12 +143,24 @@ By default, the route middleware that are assigned to each route will not be dis php artisan route:list -v ``` +You may also instruct Laravel to only show routes that begin with a given URI: + +```shell +php artisan route:list --path=api +``` + In addition, you may instruct Laravel to hide any routes that are defined by third-party packages by providing the `--except-vendor` option when executing the `route:list` command: ```shell php artisan route:list --except-vendor ``` +Likewise, you may also instruct Laravel to only show routes that are defined by third-party packages by providing the `--only-vendor` option when executing the `route:list` command: + +```shell +php artisan route:list --only-vendor +``` + ## Route Parameters @@ -257,7 +272,8 @@ The Laravel routing component allows all characters except `/` to be present wit return $search; })->where('search', '.*'); -> {note} Encoded forward slashes are only supported within the last route segment. +> **Warning** +> Encoded forward slashes are only supported within the last route segment. ## Named Routes @@ -275,7 +291,8 @@ You may also specify route names for controller actions: [UserProfileController::class, 'show'] )->name('profile'); -> {note} Route names should always be unique. +> **Warning** +> Route names should always be unique. #### Generating URLs To Named Routes @@ -288,6 +305,8 @@ Once you have assigned a name to a given route, you may use the route's name whe // Generating Redirects... return redirect()->route('profile'); + return to_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 generated URL in their correct positions: Route::get('/user/{id}/profile', function ($id) { @@ -306,7 +325,8 @@ If you pass additional parameters in the array, those key / value pairs will aut // /user/1/profile?photos=yes -> {tip} Sometimes, you may wish to specify request-wide default values for URL parameters, such as the current locale. To accomplish this, you may use the [`URL::defaults` method](/docs/{{version}}/urls#default-values). +> **Note** +> Sometimes, you may wish to specify request-wide default values for URL parameters, such as the current locale. To accomplish this, you may use the [`URL::defaults` method](/docs/{{version}}/urls#default-values). #### Inspecting The Current Route @@ -374,7 +394,8 @@ Route groups may also be used to handle subdomain routing. Subdomains may be ass }); }); -> {note} In order to ensure your subdomain routes are reachable, you should register subdomain routes before registering root domain routes. This will prevent root domain routes from overwriting subdomain routes which have the same URI path. +> **Warning** +> In order to ensure your subdomain routes are reachable, you should register subdomain routes before registering root domain routes. This will prevent root domain routes from overwriting subdomain routes which have the same URI path. ### Route Prefixes @@ -631,7 +652,8 @@ Using the `Route::fallback` method, you may define a route that will be executed // }); -> {note} The fallback route should always be the last route registered by your application. +> **Warning** +> The fallback route should always be the last route registered by your application. ## Rate Limiting @@ -644,6 +666,7 @@ Laravel includes powerful and customizable rate limiting services that you may u 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\Http\Request; use Illuminate\Support\Facades\RateLimiter; /** @@ -661,8 +684,8 @@ Rate limiters are defined using the `RateLimiter` facade's `for` method. The `fo 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 () { - return response('Custom response...', 429); + return Limit::perMinute(1000)->response(function (Request $request, array $headers) { + return response('Custom response...', 429, $headers); }); }); @@ -762,7 +785,8 @@ 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). +> **Note** +> 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 diff --git a/sail.md b/sail.md index d37bc3eba9a..cba44e2c3d1 100644 --- a/sail.md +++ b/sail.md @@ -3,7 +3,7 @@ - [Introduction](#introduction) - [Installation & Setup](#installation) - [Installing Sail Into Existing Applications](#installing-sail-into-existing-applications) - - [Configuring A Bash Alias](#configuring-a-bash-alias) + - [Configuring A Shell Alias](#configuring-a-shell-alias) - [Starting & Stopping Sail](#starting-and-stopping-sail) - [Executing Commands](#executing-sail-commands) - [Executing PHP Commands](#executing-php-commands) @@ -71,8 +71,8 @@ If you would like to develop within a [Devcontainer](https://code.visualstudio.c php artisan sail:install --devcontainer ``` - -### Configuring A Bash Alias + +### Configuring A Shell Alias By default, Sail commands are invoked using the `vendor/bin/sail` script that is included with all new Laravel applications: @@ -80,13 +80,15 @@ By default, Sail commands are invoked using the `vendor/bin/sail` script that is ./vendor/bin/sail up ``` -However, instead of repeatedly typing `vendor/bin/sail` to execute Sail commands, you may wish to configure a Bash alias that allows you to execute Sail's commands more easily: +However, instead of repeatedly typing `vendor/bin/sail` to execute Sail commands, you may wish to configure a shell alias that allows you to execute Sail's commands more easily: ```shell -alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail' +alias sail='[ -f sail ] && sh sail || sh vendor/bin/sail' ``` -Once the Bash alias has been configured, you may execute Sail commands by simply typing `sail`. The remainder of this documentation's examples will assume that you have configured this alias: +To make sure this is always available, you may add this to your shell configuration file in your home directory, such as `~/.zshrc` or `~/.bashrc`, and then restart your shell. + +Once the shell alias has been configured, you may execute Sail commands by simply typing `sail`. The remainder of this documentation's examples will assume that you have configured this alias: ```shell sail up @@ -187,7 +189,7 @@ Node commands may be executed using the `node` command while NPM commands may be ```shell sail node --version -sail npm run prod +sail npm run dev ``` If you wish, you may use Yarn instead of NPM: @@ -241,6 +243,17 @@ AWS_ENDPOINT=http://minio:9000 AWS_USE_PATH_STYLE_ENDPOINT=true ``` +In order for Laravel's Flysystem integration to generate proper URLs when using MinIO, you should define the `AWS_URL` environment variable so that it matches your application's local URL and includes the bucket name in the URL path: + +```ini +AWS_URL=http://localhost:9000/local +``` + +You may create buckets via the MinIO console, which is available at `http://localhost:8900`. The default username for the MinIO console is `sail` while the default password is `password`. + +> **Warning** +> Generating temporary storage URLs via the `temporaryUrl` method is not supported when using MinIO. + ## Running Tests @@ -258,7 +271,7 @@ The Sail `test` command is equivalent to running the `test` Artisan command: sail artisan test ``` -By default, Sail will create a dedicated `testing` database that your tests do not interfere with the current state of your database. In a default Laravel installation, Sail will also configure your `phpunit.xml` file to use this database when executing your tests: +By default, Sail will create a dedicated `testing` database so that your tests do not interfere with the current state of your database. In a default Laravel installation, Sail will also configure your `phpunit.xml` file to use this database when executing your tests: ```xml @@ -411,7 +424,8 @@ If you would like to choose the subdomain for your shared site, you may provide sail share --subdomain=my-sail-site ``` -> {tip} The `share` command is powered by [Expose](https://github.com/beyondcode/expose), an open source tunneling service by [BeyondCode](https://beyondco.de). +> **Note** +> The `share` command is powered by [Expose](https://github.com/beyondcode/expose), an open source tunneling service by [BeyondCode](https://beyondco.de). ## Debugging With Xdebug @@ -419,7 +433,7 @@ sail share --subdomain=my-sail-site Laravel Sail's Docker configuration includes support for [Xdebug](https://xdebug.org/), a popular and powerful debugger for PHP. In order to enable Xdebug, you will need to add a few variables to your application's `.env` file to [configure Xdebug](https://xdebug.org/docs/step_debug#mode). To enable Xdebug you must set the appropriate mode(s) before starting Sail: ```ini -SAIL_XDEBUG_MODE=develop,debug +SAIL_XDEBUG_MODE=develop,debug,coverage ``` #### Linux Host IP Configuration @@ -458,7 +472,8 @@ To debug your application while interacting with the application via a web brows If you're using PhpStorm, please review JetBrain's documentation regarding [zero-configuration debugging](https://www.jetbrains.com/help/phpstorm/zero-configuration-debugging.html). -> {note} Laravel Sail relies on `artisan serve` to serve your application. The `artisan serve` command only accepts the `XDEBUG_CONFIG` and `XDEBUG_MODE` variables as of Laravel version 8.53.0. Older versions of Laravel (8.52.0 and below) do not support these variables and will not accept debug connections. +> **Warning** +> Laravel Sail relies on `artisan serve` to serve your application. The `artisan serve` command only accepts the `XDEBUG_CONFIG` and `XDEBUG_MODE` variables as of Laravel version 8.53.0. Older versions of Laravel (8.52.0 and below) do not support these variables and will not accept debug connections. ## Customization diff --git a/sanctum.md b/sanctum.md index 1218328acbd..0d901c8ee3c 100644 --- a/sanctum.md +++ b/sanctum.md @@ -48,12 +48,14 @@ For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses Sanctum will only attempt to authenticate using cookies when the incoming request originates from your own SPA frontend. When Sanctum examines an incoming HTTP request, it will first check for an authentication cookie and, if none is present, Sanctum will then examine the `Authorization` header for a valid API token. -> {tip} It is perfectly fine to use Sanctum only for API token authentication or only for SPA authentication. Just because you use Sanctum does not mean you are required to use both features it offers. +> **Note** +> It is perfectly fine to use Sanctum only for API token authentication or only for SPA authentication. Just because you use Sanctum does not mean you are required to use both features it offers. ## Installation -> {tip} The most recent versions of Laravel already include Laravel Sanctum. However, if your application's `composer.json` file does not include `laravel/sanctum`, you may follow the installation instructions below. +> **Note** +> The most recent versions of Laravel already include Laravel Sanctum. However, if your application's `composer.json` file does not include `laravel/sanctum`, you may follow the installation instructions below. You may install Laravel Sanctum via the Composer package manager: @@ -73,7 +75,7 @@ Finally, you should run your database migrations. Sanctum will create one databa php artisan migrate ``` -Next, if you plan to utilize Sanctum to authenticate an SPA, you should add Sanctum's middleware to your `api` middleware group within your application's `app/Http/Kernel.php` file: +Next, if you plan to utilize Sanctum to authenticate a SPA, you should add Sanctum's middleware to your `api` middleware group within your application's `app/Http/Kernel.php` file: 'api' => [ \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, @@ -119,7 +121,8 @@ Then, you may instruct Sanctum to use your custom model via the `usePersonalAcce ## API Token Authentication -> {tip} You should not use API tokens to authenticate your own first-party SPA. Instead, use Sanctum's built-in [SPA authentication features](#spa-authentication). +> **Note** +> You should not use API tokens to authenticate your own first-party SPA. Instead, use Sanctum's built-in [SPA authentication features](#spa-authentication). ### Issuing API Tokens @@ -249,7 +252,8 @@ Sanctum also exists to provide a simple method of authenticating single page app For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses Laravel's built-in cookie based session authentication services. This approach to authentication provides the benefits of CSRF protection, session authentication, as well as protects against leakage of the authentication credentials via XSS. -> {note} In order to authenticate, your SPA and API must share the same top-level domain. However, they may be placed on different subdomains. Additionally, you should ensure that you send the `Accept: application/json` header with your request. +> **Warning** +> In order to authenticate, your SPA and API must share the same top-level domain. However, they may be placed on different subdomains. Additionally, you should ensure that you send the `Accept: application/json` header with your request. @@ -260,7 +264,8 @@ For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses First, you should configure which domains your SPA will be making requests from. You may configure these domains using the `stateful` configuration option in your `sanctum` configuration file. This configuration setting determines which domains will maintain "stateful" authentication using Laravel session cookies when making requests to your API. -> {note} If you are accessing your application via a URL that includes a port (`127.0.0.1:8000`), you should ensure that you include the port number with the domain. +> **Warning** +> If you are accessing your application via a URL that includes a port (`127.0.0.1:8000`), you should ensure that you include the port number with the domain. #### Sanctum Middleware @@ -315,7 +320,8 @@ If the login request is successful, you will be authenticated and subsequent req Of course, if your user's session expires due to lack of activity, subsequent requests to the Laravel application may receive 401 or 419 HTTP error response. In this case, you should redirect the user to your SPA's login page. -> {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. +> **Warning** +> 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 @@ -340,9 +346,9 @@ Next, in order for Pusher's authorization requests to succeed, you will need to ```js window.Echo = new Echo({ broadcaster: "pusher", - cluster: process.env.MIX_PUSHER_APP_CLUSTER, + cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, encrypted: true, - key: process.env.MIX_PUSHER_APP_KEY, + key: import.meta.env.VITE_PUSHER_APP_KEY, authorizer: (channel, options) => { return { authorize: (socketId, callback) => { @@ -399,7 +405,8 @@ Typically, you will make a request to the token endpoint from your mobile applic When the mobile application uses the token to make an API request to your application, it should pass the token in the `Authorization` header as a `Bearer` token. -> {tip} When issuing tokens for a mobile application, you are also free to specify [token abilities](#token-abilities). +> **Note** +> When issuing tokens for a mobile application, you are also free to specify [token abilities](#token-abilities). ### Protecting Routes diff --git a/scheduling.md b/scheduling.md index e32ef5ccdc2..a01caff9c00 100644 --- a/scheduling.md +++ b/scheduling.md @@ -247,7 +247,8 @@ If you are repeatedly assigning the same timezone to all of your scheduled tasks return 'America/Chicago'; } -> {note} Remember that some timezones utilize daylight savings time. When daylight saving time changes occur, your scheduled task may run twice or even not run at all. For this reason, we recommend avoiding timezone scheduling when possible. +> **Warning** +> Remember that some timezones utilize daylight savings time. When daylight saving time changes occur, your scheduled task may run twice or even not run at all. For this reason, we recommend avoiding timezone scheduling when possible. ### Preventing Task Overlaps @@ -267,7 +268,8 @@ Behind the scenes, the `withoutOverlapping` method utilizes your application's [ ### Running Tasks On One 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. +> **Warning** +> 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'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! @@ -278,6 +280,23 @@ To indicate that the task should run on only one server, use the `onOneServer` m ->at('17:00') ->onOneServer(); + +#### Naming Single Server Jobs + +Sometimes you may need to schedule the same job to be dispatched with different parameters, while still instructing Laravel to run each permutation of the job on a single server. To accomplish this, you may assign each schedule definition a unique name via the `name` method: + +```php +$schedule->job(new CheckUptime('https://laravel.com')) + ->name('check_uptime:laravel.com') + ->everyFiveMinutes() + ->onOneServer(); + +$schedule->job(new CheckUptime('https://vapor.laravel.com')) + ->name('check_uptime:vapor.laravel.com') + ->everyFiveMinutes() + ->onOneServer(); +``` + ### Background Tasks @@ -287,7 +306,8 @@ By default, multiple tasks scheduled at the same time will execute sequentially ->daily() ->runInBackground(); -> {note} The `runInBackground` method may only be used when scheduling tasks via the `command` and `exec` methods. +> **Warning** +> The `runInBackground` method may only be used when scheduling tasks via the `command` and `exec` methods. ### Maintenance Mode @@ -344,7 +364,8 @@ If you only want to email the output if the scheduled Artisan or system command ->daily() ->emailOutputOnFailure('taylor@example.com'); -> {note} The `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo`, and `appendOutputTo` methods are exclusive to the `command` and `exec` methods. +> **Warning** +> The `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo`, and `appendOutputTo` methods are exclusive to the `command` and `exec` methods. ## Task Hooks diff --git a/scout.md b/scout.md index 63b1a6eea11..d837e468127 100644 --- a/scout.md +++ b/scout.md @@ -8,6 +8,7 @@ - [Configuring Model Indexes](#configuring-model-indexes) - [Configuring Searchable Data](#configuring-searchable-data) - [Configuring The Model ID](#configuring-the-model-id) + - [Configuring Search Engines Per Model](#configuring-search-engines-per-model) - [Identifying Users](#identifying-users) - [Database / Collection Engines](#database-and-collection-engines) - [Database Engine](#database-engine) @@ -98,7 +99,8 @@ For more information regarding MeiliSearch, please consult the [MeiliSearch docu In addition, you should ensure that you install a version of `meilisearch/meilisearch-php` that is compatible with your MeiliSearch binary version by reviewing [MeiliSearch's documentation regarding binary compatibility](https://github.com/meilisearch/meilisearch-php#-compatibility-with-meilisearch). -> {note} When upgrading Scout on an application that utilizes MeiliSearch, you should always [review any additional breaking changes](https://github.com/meilisearch/MeiliSearch/releases) to the MeiliSearch service itself. +> **Warning** +> When upgrading Scout on an application that utilizes MeiliSearch, you should always [review any additional breaking changes](https://github.com/meilisearch/MeiliSearch/releases) to the MeiliSearch service itself. ### Queueing @@ -111,6 +113,13 @@ Once you have configured a queue driver, set the value of the `queue` option in Even when the `queue` option is set to `false`, it's important to remember that some Scout drivers like Algolia and Meilisearch always index records asynchronously. Meaning, even though the index operation has completed within your Laravel application, the search engine itself may not reflect the new and updated records immediately. +To specify the connection and queue that your Scout jobs utilize, you may define the `queue` configuration option as an array: + + 'queue' => [ + 'connection' => 'redis', + 'queue' => 'scout' + ], + ## Configuration @@ -175,7 +184,7 @@ By default, the entire `toArray` form of a given model will be persisted to its ### Configuring The Model ID -By default, Scout will use the primary key of the model as model's unique ID / key that is stored in the search index. If you need to customize this behavior, you may override the `getScoutKey` and the `getScoutKeyName` methods on the model: +By default, Scout will use the primary key of the model as the model's unique ID / key that is stored in the search index. If you need to customize this behavior, you may override the `getScoutKey` and the `getScoutKeyName` methods on the model: +### Configuring Search Engines Per Model + +When searching, Scout will typically use the default search engine specified in your application's `scout` configuration file. However, the search engine for a particular model can be changed by overriding the `searchableUsing` method on the model: + + engine('meilisearch'); + } + } + ### Identifying Users @@ -226,7 +263,8 @@ Enabling this feature this will also pass the request's IP address and your auth ### Database Engine -> {note} The database engine currently supports MySQL and PostgreSQL. +> **Warning** +> The database engine currently supports MySQL and PostgreSQL. If your application interacts with small to medium sized databases or has a light workload, you may find it more convenient to get started with Scout's "database" engine. The database engine will use "where like" clauses and full text indexes when filtering results from your existing database to determine the applicable search results for your query. @@ -266,7 +304,8 @@ public function toSearchableArray() } ``` -> {note} Before specifying that a column should use full text query constraints, ensure that the column has been assigned a [full text index](/docs/{{version}}/migrations#available-index-types). +> **Warning** +> Before specifying that a column should use full text query constraints, ensure that the column has been assigned a [full text index](/docs/{{version}}/migrations#available-index-types). ### Collection Engine @@ -351,7 +390,8 @@ Or, if you already have a collection of Eloquent models in memory, you may call $orders->searchable(); -> {tip} The `searchable` method can be considered an "upsert" operation. In other words, if the model record is already in your index, it will be updated. If it does not exist in the search index, it will be added to the index. +> **Note** +> The `searchable` method can be considered an "upsert" operation. In other words, if the model record is already in your index, it will be updated. If it does not exist in the search index, it will be added to the index. ### Updating Records @@ -429,7 +469,8 @@ Sometimes you may need to only make a model searchable under certain conditions. The `shouldBeSearchable` method is only applied when manipulating models through the `save` and `create` methods, queries, or relationships. Directly making models or collections searchable using the `searchable` method will override the result of the `shouldBeSearchable` method. -> {note} The `shouldBeSearchable` method is not applicable when using Scout's "database" engine, as all searchable data is always stored in the database. To achieve similar behavior when using the database engine, you should use [where clauses](#where-clauses) instead. +> **Warning** +> The `shouldBeSearchable` method is not applicable when using Scout's "database" engine, as all searchable data is always stored in the database. To achieve similar behavior when using the database engine, you should use [where clauses](#where-clauses) instead. ## Searching @@ -513,6 +554,9 @@ Of course, if you would like to retrieve the pagination results as JSON, you may return Order::search($request->input('query'))->paginate(15); }); +> **Warning** +> Since search engines are not aware of your Eloquent model's global scope definitions, you should not utilize global scopes in applications that utilize Scout pagination. Or, you should recreate the global scope's constraints when searching via Scout. + ### Soft Deleting @@ -530,7 +574,8 @@ When this configuration option is `true`, Scout will not remove soft deleted mod // Only include trashed records when retrieving results... $orders = Order::search('Star Trek')->onlyTrashed()->get(); -> {tip} When a soft deleted model is permanently deleted using `forceDelete`, Scout will remove it from the search index automatically. +> **Note** +> When a soft deleted model is permanently deleted using `forceDelete`, Scout will remove it from the search index automatically. ### Customizing Engine Searches diff --git a/seeding.md b/seeding.md index 1079248d02b..0d1eec442e3 100644 --- a/seeding.md +++ b/seeding.md @@ -12,7 +12,8 @@ Laravel includes the ability to seed your database with 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. +> **Note** +> [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled during database seeding. ## Writing Seeders @@ -23,7 +24,7 @@ To generate a seeder, execute the `make:seeder` [Artisan command](/docs/{{versio php artisan make:seeder UserSeeder ``` -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#defining-model-factories). +A seeder class only contains one method by default: `run`. This method is called when the `db:seed` [Artisan command](/docs/{{version}}/artisan) is executed. Within the `run` method, you may insert data into your database however you wish. You may use the [query builder](/docs/{{version}}/queries) to manually insert data or you may use [Eloquent model factories](/docs/{{version}}/eloquent-factories). As an example, let's modify the default `DatabaseSeeder` class and add a database insert statement to the `run` method: @@ -53,12 +54,13 @@ As an example, let's modify the default `DatabaseSeeder` class and add a databas } } -> {tip} You may type-hint any dependencies you need within the `run` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). +> **Note** +> You may type-hint any dependencies you need within the `run` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). ### Using Model Factories -Of course, manually specifying the attributes for each model seed is cumbersome. Instead, you can use [model factories](/docs/{{version}}/database-testing#defining-model-factories) to conveniently generate large amounts of database records. First, review the [model factory documentation](/docs/{{version}}/database-testing#defining-model-factories) to learn how to define your factories. +Of course, manually specifying the attributes for each model seed is cumbersome. Instead, you can use [model factories](/docs/{{version}}/eloquent-factories) to conveniently generate large amounts of database records. First, review the [model factory documentation](/docs/{{version}}/eloquent-factories) to learn how to define your factories. For example, let's create 50 users that each has one related post: diff --git a/session.md b/session.md index 9fdf9ec572c..66b19b40459 100644 --- a/session.md +++ b/session.md @@ -39,7 +39,8 @@ The session `driver` configuration option defines where session data will be sto
-> {tip} The array driver is primarily used during [testing](/docs/{{version}}/testing) and prevents the data stored in the session from being persisted. +> **Note** +> The array driver is primarily used during [testing](/docs/{{version}}/testing) and prevents the data stored in the session from being persisted. ### Driver Prerequisites @@ -71,7 +72,8 @@ php artisan migrate 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. +> **Note** +> In the `session` configuration file, the `connection` option may be used to specify which Redis connection is used by the session. ## Interacting With The Session @@ -129,7 +131,8 @@ You may also use the global `session` PHP function to retrieve and store data in session(['key' => 'value']); }); -> {tip} There is little practical difference between using the session via an HTTP request instance versus using the global `session` helper. Both methods are [testable](/docs/{{version}}/testing) via the `assertSessionHas` method which is available in all of your test cases. +> **Note** +> There is little practical difference between using the session via an HTTP request instance versus using the global `session` helper. Both methods are [testable](/docs/{{version}}/testing) via the `assertSessionHas` method which is available in all of your test cases. #### Retrieving All Session Data @@ -243,7 +246,8 @@ If you need to regenerate the session ID and remove all data from the session in ## Session Blocking -> {note} To utilize session blocking, your application must be using a cache driver that supports [atomic locks](/docs/{{version}}/cache#atomic-locks). Currently, those cache drivers include the `memcached`, `dynamodb`, `redis`, and `database` drivers. In addition, you may not use the `cookie` session driver. +> **Warning** +> To utilize session blocking, your application must be using a cache driver that supports [atomic locks](/docs/{{version}}/cache#atomic-locks). Currently, those cache drivers include the `memcached`, `dynamodb`, `redis`, and `database` drivers. In addition, you may not use the `cookie` session driver. By default, Laravel allows requests using the same session to execute concurrently. So, for example, if you use a JavaScript HTTP library to make two HTTP requests to your application, they will both execute at the same time. For many applications, this is not a problem; however, session data loss can occur in a small subset of applications that make concurrent requests to two different application endpoints which both write data to the session. @@ -289,7 +293,8 @@ If none of the existing session drivers fit your application's needs, Laravel ma public function gc($lifetime) {} } -> {tip} Laravel does not ship with a directory to contain your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `MongoSessionHandler`. +> **Note** +> Laravel does not ship with a directory to contain your extensions. You are free to place them anywhere you like. In this example, we have created an `Extensions` directory to house the `MongoSessionHandler`. Since the purpose of these methods is not readily understandable, let's quickly cover what each of the methods do: diff --git a/socialite.md b/socialite.md index 35a352deb3e..88143ae55d9 100644 --- a/socialite.md +++ b/socialite.md @@ -16,7 +16,8 @@ In addition to typical, form based authentication, Laravel also provides a simple, convenient way to authenticate with OAuth providers using [Laravel Socialite](https://github.com/laravel/socialite). Socialite currently supports authentication via Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, and Bitbucket. -> {tip} Adapters for other platforms are available via the community driven [Socialite Providers](https://socialiteproviders.com/) website. +> **Note** +> Adapters for other platforms are available via the community driven [Socialite Providers](https://socialiteproviders.com/) website. ## Installation @@ -45,7 +46,8 @@ These credentials should be placed in your application's `config/services.php` c 'redirect' => 'http://example.com/callback-url', ], -> {tip} If the `redirect` option contains a relative path, it will automatically be resolved to a fully qualified URL. +> **Note** +> If the `redirect` option contains a relative path, it will automatically be resolved to a fully qualified URL. ## Authentication @@ -95,7 +97,8 @@ Once the user has been retrieved from the OAuth provider, you may determine if t return redirect('/dashboard'); }); -> {tip} For more information regarding what user information is available from specific OAuth providers, please consult the documentation on [retrieving user details](#retrieving-user-details). +> **Note** +> For more information regarding what user information is available from specific OAuth providers, please consult the documentation on [retrieving user details](#retrieving-user-details). ### Access Scopes @@ -125,7 +128,8 @@ A number of OAuth providers support other optional parameters on the redirect re ->with(['hd' => 'example.com']) ->redirect(); -> {note} When using the `with` method, be careful not to pass any reserved keywords such as `state` or `response_type`. +> **Warning** +> When using the `with` method, be careful not to pass any reserved keywords such as `state` or `response_type`. ## Retrieving User Details @@ -183,4 +187,5 @@ The `stateless` method may be used to disable session state verification. This i return Socialite::driver('google')->stateless()->user(); -> {note} Stateless authentication is not available for the Twitter OAuth 1.0 driver. +> **Warning** +> Stateless authentication is not available for the Twitter OAuth 1.0 driver. diff --git a/starter-kits.md b/starter-kits.md index 18f7cce7549..6ce0ab48a72 100644 --- a/starter-kits.md +++ b/starter-kits.md @@ -3,6 +3,7 @@ - [Introduction](#introduction) - [Laravel Breeze](#laravel-breeze) - [Installation](#laravel-breeze-installation) + - [Breeze & Blade](#breeze-and-blade) - [Breeze & React / Vue](#breeze-and-inertia) - [Breeze & Next.js / API](#breeze-and-next) - [Laravel Jetstream](#laravel-jetstream) @@ -17,51 +18,49 @@ While you are welcome to use these starter kits, they are not required. You are ## Laravel Breeze -[Laravel Breeze](https://github.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 default view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). +[Laravel Breeze](https://github.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 default view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). Or, Breeze can scaffold your application using Vue or React and [Inertia](https://inertiajs.com). -Breeze provides a wonderful starting point for beginning a fresh Laravel application and is also great choice for projects that plan to take their Blade templates to the next level with [Laravel Livewire](https://laravel-livewire.com). +Breeze provides a wonderful starting point for beginning a fresh Laravel application and is also a great choice for projects that plan to take their Blade templates to the next level with [Laravel Livewire](https://laravel-livewire.com). ### Installation -First, you should [create a new Laravel application](/docs/{{version}}/installation), configure your database, and run your [database migrations](/docs/{{version}}/migrations): +First, you should [create a new Laravel application](/docs/{{version}}/installation), configure your database, and run your [database migrations](/docs/{{version}}/migrations). Once you have created a new Laravel application, you may install Laravel Breeze using Composer: ```shell -curl -s https://laravel.build/example-app | bash - -cd example-app - -php artisan migrate +composer require laravel/breeze --dev ``` -Once you have created a new Laravel application, you may install Laravel Breeze using Composer: +Once Breeze is installed, you may scaffold your application using one of the Breeze "stacks" discussed in the documentation below. -```shell -composer require laravel/breeze --dev -``` + +### Breeze & Blade + +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 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: +The default Breeze "stack" is the Blade stack, which utilizes simple [Blade templates](/docs/{{version}}/blade) to render your application's frontend. The Blade stack may be installed by invoking the `breeze:install` command with no other additional arguments. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: ```shell php artisan breeze:install +php artisan migrate npm install npm run dev -php artisan migrate ``` 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). +> **Note** +> To learn more about compiling your application's CSS and JavaScript, check out Laravel's [Vite documentation](/docs/{{version}}/vite#running-vite). ### Breeze & React / Vue -Laravel Breeze also offers React and Vue scaffolding via an [Inertia.js](https://inertiajs.com) frontend implementation. Inertia allows you to build modern, single-page React and Vue applications using classic server-side routing and controllers. +Laravel Breeze also offers React and Vue scaffolding via an [Inertia](https://inertiajs.com) frontend implementation. Inertia allows you to build modern, single-page React and Vue applications using classic server-side routing and controllers. -Inertia lets you enjoy the frontend power of React and Vue combined with the incredible backend productivity of Laravel. To use an Inertia stack, specify `vue` or `react` as your desired stack when executing the `breeze:install` Artisan command: +Inertia lets you enjoy the frontend power of React and Vue combined with the incredible backend productivity of Laravel and lightning-fast [Vite](https://vitejs.dev) compilation. To use an Inertia stack, specify `vue` or `react` as your desired stack when executing the `breeze:install` Artisan command. After Breeze's scaffolding is installed, you should also compile your application's frontend assets: ```shell php artisan breeze:install vue @@ -70,11 +69,16 @@ php artisan breeze:install vue php artisan breeze:install react +php artisan migrate npm install npm run dev -php artisan migrate ``` +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. + + +#### Server-Side Rendering + If you would like Breeze to scaffold support for [Inertia SSR](https://inertiajs.com/server-side-rendering), you may provide the `ssr` option when invoking the `breeze:install` command: ```shell @@ -105,6 +109,6 @@ Finally, you are ready to pair this backend with the frontend of your choice. A 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. +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](https://inertiajs.com) driven frontend scaffolding. Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/2.x/introduction.html). diff --git a/structure.md b/structure.md index bfb9525cf5c..a7cf692f50b 100644 --- a/structure.md +++ b/structure.md @@ -76,7 +76,7 @@ The `resources` directory contains your [views](/docs/{{version}}/views) as well 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 then it is likely that 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 all 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. 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. @@ -110,7 +110,8 @@ The `app` directory contains a variety of additional directories such as `Consol A variety of other directories will be generated inside the `app` directory as you use the `make` Artisan commands to generate classes. So, for example, the `app/Jobs` directory will not exist until you execute the `make:job` Artisan command to generate a job class. -> {tip} Many of the classes in the `app` directory can be generated by Artisan via commands. To review the available commands, run the `php artisan list make` command in your terminal. +> **Note** +> Many of the classes in the `app` directory can be generated by Artisan via commands. To review the available commands, run the `php artisan list make` command in your terminal. #### The Broadcasting Directory diff --git a/telescope.md b/telescope.md index 3ccf67e1fb1..77d2fd39fe4 100644 --- a/telescope.md +++ b/telescope.md @@ -142,7 +142,8 @@ The Telescope dashboard may be accessed at the `/telescope` route. By default, y }); } -> {note} You should ensure you change your `APP_ENV` environment variable to `production` in your production environment. Otherwise, your Telescope installation will be publicly available. +> **Warning** +> You should ensure you change your `APP_ENV` environment variable to `production` in your production environment. Otherwise, your Telescope installation will be publicly available. ## Upgrading Telescope diff --git a/testing.md b/testing.md index 837e0947f58..2545849f6b2 100644 --- a/testing.md +++ b/testing.md @@ -57,7 +57,8 @@ php artisan make:test UserTest --pest php artisan make:test UserTest --unit --pest ``` -> {tip} Test stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). +> **Note** +> Test stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization). Once the test has been generated, you may define test methods as you normally would using [PHPUnit](https://phpunit.de). To run your tests, execute the `vendor/bin/phpunit` or `php artisan test` command from your terminal: @@ -80,7 +81,8 @@ Once the test has been generated, you may define test methods as you normally wo } } -> {note} If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. +> **Warning** +> If you define your own `setUp` / `tearDown` methods within a test class, be sure to call the respective `parent::setUp()` / `parent::tearDown()` methods on the parent class. ## Running Tests @@ -118,7 +120,8 @@ By default, Laravel will create as many processes as there are available CPU cor php artisan test --parallel --processes=4 ``` -> {note} When running tests in parallel, some PHPUnit options (such as `--do-not-cache-result`) may not be available. +> **Warning** +> When running tests in parallel, some PHPUnit options (such as `--do-not-cache-result`) may not be available. #### Parallel Testing & Databases @@ -188,7 +191,8 @@ If you would like to access the current parallel process "token" from any other ### Reporting Test Coverage -> {note} This feature requires [Xdebug](https://xdebug.org) or [PCOV](https://pecl.php.net/package/pcov). +> **Warning** +> This feature requires [Xdebug](https://xdebug.org) or [PCOV](https://pecl.php.net/package/pcov). When running your application tests, you may want to determine whether your test cases are actually covering the application code and how much application code is used when running your tests. To accomplish this, you may provide the `--coverage` option when invoking the `test` command: diff --git a/upgrade.md b/upgrade.md index b94b96741ce..a2ea821fbea 100644 --- a/upgrade.md +++ b/upgrade.md @@ -37,7 +37,8 @@ #### Estimated Upgrade Time: 30 Minutes -> {tip} We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. +> **Note** +> We attempt to document every possible breaking change. Since some of these breaking changes are in obscure parts of the framework only a portion of these changes may actually affect your application. Want to save time? You can use [Laravel Shift](https://laravelshift.com/) to help automate your application upgrades. ### Updating Dependencies @@ -59,7 +60,7 @@ You should update the following dependencies in your application's `composer.jso
-In addition, please replace `facade/ignition` with `"spatie/laravel-ignition": "^1.0"` in your application's `composer.json` file. +In addition, please replace `facade/ignition` with `"spatie/laravel-ignition": "^1.0"` and `pusher/pusher-php-server` (if applicable) with `"pusher/pusher-php-server": "^5.0"` in your application's `composer.json` file. Furthermore, the following first-party packages have received new major releases to support Laravel 9.x. If applicable, you should read their individual upgrade guides before upgrading: @@ -128,6 +129,12 @@ The exception handler's `ignore` method is now `public` instead of `protected`. public function ignore(string $class); ``` +#### Exception Handler Contract Binding + +**Likelihood Of Impact: Very Low** + +Previously, in order to override the default Laravel exception handler, custom implementations were bound into the service container using the `\App\Exceptions\Handler::class` type. However, you should now bind custom implementations using the `\Illuminate\Contracts\Debug\ExceptionHandler::class` type. + ### Blade #### Lazy Collections & The `$loop` Variable @@ -449,7 +456,7 @@ The [HTTP client](/docs/{{version}}/http-client) now has a default timeout of 30 If you wish to specify a longer timeout for a given request, you may do so using the `timeout` method: - $response = Http::timeout(120)->get(...); + $response = Http::timeout(120)->get(/* ... */); #### HTTP Fake & Middleware @@ -508,7 +515,8 @@ Various SwiftMailer related methods, some of which were undocumented, have been ); }); -> {note} Please thoroughly review the [Symfony Mailer documentation](https://symfony.com/doc/6.0/mailer.html#creating-sending-messages) for all possible interactions with the `Symfony\Component\Mime\Email` object. +> **Warning** +> Please thoroughly review the [Symfony Mailer documentation](https://symfony.com/doc/6.0/mailer.html#creating-sending-messages) for all possible interactions with the `Symfony\Component\Mime\Email` object. The list below contains a more thorough overview of renamed methods. Many of these methods are low-level methods used to interact with SwiftMailer / Symfony Mailer directly, so may not be commonly used within most Laravel applications: @@ -584,7 +592,8 @@ Defining stream options for the SMTP transport is no longer supported. Instead, To learn more about the available configuration options, please review the [Symfony Mailer documentation](https://symfony.com/doc/6.0/mailer.html#transport-setup). -> {note} In spite of the example above, you are not generally advised to disable SSL verification since it introduces the possibility of "man-in-the-middle" attacks. +> **Warning** +> In spite of the example above, you are not generally advised to disable SSL verification since it introduces the possibility of "man-in-the-middle" attacks. #### SMTP `auth_mode` diff --git a/valet.md b/valet.md index b7c6207399b..f49bd01e3a3 100644 --- a/valet.md +++ b/valet.md @@ -68,7 +68,8 @@ However, you may extend Valet with your own [custom drivers](#custom-valet-drive ## Installation -> {note} Valet requires macOS and [Homebrew](https://brew.sh/). Before installation, you should make sure that no other programs such as Apache or Nginx are binding to your local machine's port 80. +> **Warning** +> Valet requires macOS and [Homebrew](https://brew.sh/). Before installation, you should make sure that no other programs such as Apache or Nginx are binding to your local machine's port 80. To get started, you first need to ensure that Homebrew is up to date using the `update` command: @@ -117,7 +118,8 @@ php@7.2 Once this file has been created, you may simply execute the `valet use` command and the command will determine the site's preferred PHP version by reading the file. -> {note} Valet only serves one PHP version at a time, even if you have multiple PHP versions installed. +> **Warning** +> Valet only serves one PHP version at a time, even if you have multiple PHP versions installed. #### Database @@ -270,7 +272,8 @@ valet share To stop sharing your site, you may press `Control + C`. Sharing your site using Ngrok requires you to [create an Ngrok account](https://dashboard.ngrok.com/signup) and [setup an authentication token](https://dashboard.ngrok.com/get-started/your-authtoken). -> {tip} You may pass additional Ngrok parameters to the share command, such as `valet share --region=eu`. For more information, consult the [ngrok documentation](https://ngrok.com/docs). +> **Note** +> You may pass additional Ngrok parameters to the share command, such as `valet share --region=eu`. For more information, consult the [ngrok documentation](https://ngrok.com/docs). ### Sharing Sites Via Expose @@ -395,7 +398,8 @@ The `isStaticFile` should determine if the incoming request is for a file that i return false; } -> {note} The `isStaticFile` method will only be called if the `serves` method returns `true` for the incoming request and the request URI is not `/`. +> **Warning** +> The `isStaticFile` method will only be called if the `serves` method returns `true` for the incoming request and the request URI is not `/`. #### The `frontControllerPath` Method diff --git a/validation.md b/validation.md index b2259c324d6..5b328a88e54 100644 --- a/validation.md +++ b/validation.md @@ -8,6 +8,7 @@ - [Displaying The Validation Errors](#quick-displaying-the-validation-errors) - [Repopulating Forms](#repopulating-forms) - [A Note On Optional Fields](#a-note-on-optional-fields) + - [Validation Error Response Format](#validation-error-response-format) - [Form Request Validation](#form-request-validation) - [Creating Form Requests](#creating-form-requests) - [Authorizing Form Requests](#authorizing-form-requests) @@ -28,6 +29,7 @@ - [Validating Arrays](#validating-arrays) - [Validating Nested Array Input](#validating-nested-array-input) - [Error Message Indexes & Positions](#error-message-indexes-and-positions) +- [Validating Files](#validating-files) - [Validating Passwords](#validating-passwords) - [Custom Validation Rules](#custom-validation-rules) - [Using Rule Objects](#using-rule-objects) @@ -99,7 +101,7 @@ Next, let's take a look at a simple controller that handles incoming requests to 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 `Illuminate\Validation\ValidationException` exception will be thrown and the proper error response will automatically be sent back to the user. -If validation fails during a traditional HTTP request, a redirect response to the previous URL will be generated. If the incoming request is an XHR request, a JSON response containing the validation error messages will be returned. +If validation fails during a traditional HTTP request, a redirect response to the previous URL will be generated. If the incoming request is an XHR request, a [JSON response containing the validation error messages](#validation-error-response-format) will be returned. To get a better understanding of the `validate` method, let's jump back into the `store` method: @@ -195,14 +197,14 @@ So, in our example, the user will be redirected to our controller's `create` met #### Customizing The Error Messages -Laravel's built-in validation rules each has an error message that is located in your application's `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. +Laravel's built-in validation rules each have an error message that is located in your application's `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). #### 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](#validation-error-response-format). This JSON response will be sent with a 422 HTTP status code. #### The `@error` Directive @@ -258,6 +260,34 @@ 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. + +### Validation Error Response Format + +When your application throws a `Illuminate\Validation\ValidationException` exception and the incoming HTTP request is expecting a JSON response, Laravel will automatically format the error messages for you and return a `422 Unprocessable Entity` HTTP response. + +Below, you can review an example of the JSON response format for validation errors. Note that nested error keys are flattened into "dot" notation format: + +```json +{ + "message": "The team name must be a string. (and 4 more errors)", + "errors": { + "team_name": [ + "The team name must be a string.", + "The team name must be at least 1 characters." + ], + "authorization.role": [ + "The selected authorization.role is invalid." + ], + "users.0.email": [ + "The users.0.email field is required." + ], + "users.2.email": [ + "The users.2.email must be a valid email address." + ] + } +} +``` + ## Form Request Validation @@ -287,7 +317,8 @@ As you might have guessed, the `authorize` method is responsible for determining ]; } -> {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). +> **Note** +> 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: @@ -309,7 +340,7 @@ So, how are the validation rules evaluated? All you need to do is type-hint the $validated = $request->safe()->except(['name', 'email']); } -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. +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](#validation-error-response-format). #### Adding After Hooks To Form Requests @@ -406,7 +437,8 @@ If you plan to handle authorization logic for the request in another part of you return true; } -> {tip} You may type-hint any dependencies you need within the `authorize` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). +> **Note** +> You may type-hint any dependencies you need within the `authorize` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container). ### Customizing The Error Messages @@ -522,7 +554,7 @@ The `stopOnFirstFailure` method will inform the validator that it should stop va ### Automatic Redirection -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: +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](#validation-error-response-format): Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', @@ -590,7 +622,7 @@ Many of Laravel's built-in error messages include an `:attribute` placeholder th 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 = Validator::make(/* ... */); $validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { @@ -691,7 +723,7 @@ The `has` method may be used to determine if any error messages exist for a give ### Specifying Custom Messages In Language Files -Laravel's built-in validation rules each has an error message that is located in your application's `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. +Laravel's built-in validation rules each have an error message that is located in your application's `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). @@ -791,6 +823,8 @@ Below is a list of all available validation rules and their function: [Digits Between](#rule-digits-between) [Dimensions (Image Files)](#rule-dimensions) [Distinct](#rule-distinct) +[Doesnt Start With](#rule-doesnt-start-with) +[Doesnt End With](#rule-doesnt-end-with) [Email](#rule-email) [Ends With](#rule-ends-with) [Enum](#rule-enum) @@ -814,9 +848,11 @@ Below is a list of all available validation rules and their function: [Less Than Or Equal](#rule-lte) [MAC Address](#rule-mac) [Max](#rule-max) +[Max Digits](#rule-max-digits) [MIME Types](#rule-mimetypes) [MIME Type By File Extension](#rule-mimes) [Min](#rule-min) +[Min Digits](#rule-min-digits) [Multiple Of](#multiple-of) [Not In](#rule-not-in) [Not Regex](#rule-not-regex) @@ -913,7 +949,7 @@ When additional values are provided to the `array` rule, each key in the input a ]; Validator::make($input, [ - 'user' => 'array:username,locale', + 'user' => 'array:name,username', ]); In general, you should always specify the array keys that are allowed to be present within your array. @@ -942,7 +978,7 @@ The field under validation must be a value preceding or equal to the given date. #### between:_min_,_max_ -The field under validation must have a size between the given _min_ and _max_. Strings, numerics, arrays, and files are evaluated in the same fashion as the [`size`](#rule-size) rule. +The field under validation must have a size between the given _min_ and _max_ (inclusive). Strings, numerics, arrays, and files are evaluated in the same fashion as the [`size`](#rule-size) rule. #### boolean @@ -994,12 +1030,12 @@ The field under validation must have a different value than _field_. #### digits:_value_ -The field under validation must be _numeric_ and must have an exact length of _value_. +The integer under validation must have an exact length of _value_. #### digits_between:_min_,_max_ -The field under validation must be _numeric_ and must have a length between the given _min_ and _max_. +The integer validation must have a length between the given _min_ and _max_. #### dimensions @@ -1041,6 +1077,16 @@ You may add `ignore_case` to the validation rule's arguments to make the rule ig 'foo.*.id' => 'distinct:ignore_case' + +#### doesnt_start_with:_foo_,_bar_,... + +The field under validation must not start with one of the given values. + + +#### doesnt_end_with:_foo_,_bar_,... + +The field under validation must not end with one of the given values. + #### email @@ -1062,7 +1108,8 @@ The example above will apply the `RFCValidation` and `DNSCheckValidation` valida 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. +> **Warning** +> The `dns` and `spoof` validators require the PHP `intl` extension. #### ends_with:_foo_,_bar_,... @@ -1081,7 +1128,8 @@ The `Enum` rule is a class based rule that validates whether the field under val 'status' => [new Enum(ServerStatus::class)], ]); -> {note} Enums are only available on PHP 8.1+. +> **Warning** +> Enums are only available on PHP 8.1+. #### exclude @@ -1219,8 +1267,8 @@ When the `in` rule is combined with the `array` rule, each value in the input ar 'airports' => [ 'required', 'array', - Rule::in(['NYC', 'LIT']), ], + 'airports.*' => Rule::in(['NYC', 'LIT']), ]); @@ -1233,7 +1281,8 @@ The field under validation must exist in _anotherfield_'s values. The field under validation must be an integer. -> {note} This validation rule does not verify that the input is of the "integer" variable type, only that the input is of a type accepted by PHP's `FILTER_VALIDATE_INT` rule. If you need to validate the input as being a number please use this rule in combination with [the `numeric` validation rule](#rule-numeric). +> **Warning** +> This validation rule does not verify that the input is of the "integer" variable type, only that the input is of a type accepted by PHP's `FILTER_VALIDATE_INT` rule. If you need to validate the input as being a number please use this rule in combination with [the `numeric` validation rule](#rule-numeric). #### ip @@ -1275,6 +1324,11 @@ The field under validation must be a MAC address. The field under validation must be less than or equal to a maximum _value_. Strings, numerics, arrays, and files are evaluated in the same fashion as the [`size`](#rule-size) rule. + +#### max_digits:_value_ + +The integer under validation must have a maximum length of _value_. + #### mimetypes:_text/plain_,... @@ -1303,12 +1357,18 @@ Even though you only need to specify the extensions, this rule actually validate The field under validation must have a minimum _value_. Strings, numerics, arrays, and files are evaluated in the same fashion as the [`size`](#rule-size) rule. + +#### min_digits:_value_ + +The integer under validation must have a minimum length of _value_. + #### multiple_of:_value_ The field under validation must be a multiple of _value_. -> {note} The [`bcmath` PHP extension](https://www.php.net/manual/en/book.bc.php) is required in order to use the `multiple_of` rule. +> **Warning** +> The [`bcmath` PHP extension](https://www.php.net/manual/en/book.bc.php) is required in order to use the `multiple_of` rule. #### not_in:_foo_,_bar_,... @@ -1331,7 +1391,8 @@ 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 your validation rules using an array instead of using `|` delimiters, especially if the regular expression contains a `|` character. +> **Warning** +> 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. #### nullable @@ -1348,7 +1409,8 @@ The field under validation must be [numeric](https://www.php.net/manual/en/funct The field under validation must match the authenticated user's password. -> {note} This rule was renamed to `current_password` with the intention of removing it in Laravel 9. Please use the [Current Password](#rule-current-password) rule instead. +> **Warning** +> This rule was renamed to `current_password` with the intention of removing it in Laravel 9. Please use the [Current Password](#rule-current-password) rule instead. #### present @@ -1358,12 +1420,12 @@ The field under validation must be present in the input data but can be empty. #### prohibited -The field under validation must be empty or not present. +The field under validation must be an empty string or not present. #### prohibited_if:_anotherfield_,_value_,... -The field under validation must be empty or not present if the _anotherfield_ field is equal to any _value_. +The field under validation must be an empty string or not present if the _anotherfield_ field is equal to any _value_. If complex conditional prohibition logic is required, you may utilize the `Rule::prohibitedIf` method. This method accepts a boolean or a closure. When given a closure, the closure should return `true` or `false` to indicate if the field under validation should be prohibited: @@ -1381,7 +1443,7 @@ If complex conditional prohibition logic is required, you may utilize the `Rule: #### prohibited_unless:_anotherfield_,_value_,... -The field under validation must be empty or not present unless the _anotherfield_ field is equal to any _value_. +The field under validation must be an empty string or not present unless the _anotherfield_ field is equal to any _value_. #### prohibits:_anotherfield_,... @@ -1395,7 +1457,8 @@ 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 `|` delimiters, especially if the regular expression contains a `|` character. +> **Warning** +> 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. #### required @@ -1533,7 +1596,8 @@ 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. +> **Warning** +> 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 also pass the entire model instance. Laravel will automatically extract the key from the model: @@ -1598,7 +1662,8 @@ In some situations, you may wish to run validation checks against a field **only In the example above, the `email` field will only be validated if it is present in the `$data` array. -> {tip} If you are attempting to validate a field that should always be present but may be empty, check out [this note on optional fields](#a-note-on-optional-fields). +> **Note** +> If you are attempting to validate a field that should always be present but may be empty, check out [this note on optional fields](#a-note-on-optional-fields). #### Complex Conditional Validation @@ -1624,7 +1689,8 @@ 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 under validation. +> **Note** +> 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. #### Complex Conditional Array Validation @@ -1747,6 +1813,48 @@ When validating arrays, you may want to reference the index or position of a par Given the example above, validation will fail and the user will be presented with the following error of _"Please describe photo #2."_ + +## Validating Files + +Laravel provides a variety of validation rules that may be used to validate uploaded files, such as `mimes`, `image`, `min`, and `max`. While you are free to specify these rules individually when validating files, Laravel also offers a fluent file validation rule builder that you may find convenient: + + use Illuminate\Support\Facades\Validator; + use Illuminate\Validation\Rules\File; + + Validator::validate($input, [ + 'attachment' => [ + 'required', + File::types(['mp3', 'wav']) + ->min(1024) + ->max(12 * 1024), + ], + ]); + +If your application accepts images uploaded by your users, you may use the `File` rule's `image` constructor method to indicate that the uploaded file should be an image. In addition, the `dimensions` rule may be used to limit the dimensions of the image: + + use Illuminate\Support\Facades\Validator; + use Illuminate\Validation\Rules\File; + + Validator::validate($input, [ + 'photo' => [ + 'required', + File::image() + ->min(1024) + ->max(12 * 1024) + ->dimensions(Rule::dimensions()->maxWidth(1000)->maxHeight(500)), + ], + ]); + +> **Note** +> More information regarding validating image dimensions may be found in the [dimension rule documentation](#rule-dimensions). + + +#### File Types + +Even though you only need to specify the extensions when invoking the `types` method, this method 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: + +[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) + ## Validating Passwords @@ -1844,54 +1952,35 @@ Occasionally, you may want to attach additional validation rules to your default 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: ```shell -php artisan make:rule Uppercase +php artisan make:rule Uppercase --invokable ``` -Once the rule has been created, we are ready to define its behavior. A rule object contains two methods: `passes` and `message`. The `passes` method receives the attribute value and name, and should return `true` or `false` depending on whether the attribute value is valid or not. The `message` method should return the validation error message that should be used when validation fails: +Once the rule has been created, we are ready to define its behavior. A rule object contains a single method: `__invoke`. This method receives the attribute name, its value, and a callback that should be invoked on failure with the validation error message: ['required', 'string', new Uppercase], ]); +#### Translating Validation Messages + +Instead of providing a literal error message to the `$fail` closure, you may also provide a [translation string key](/docs/{{version}}/localization) and instruct Laravel to translate the error message: + + if (strtoupper($value) !== $value) { + $fail('validation.uppercase')->translate(); + } + +If necessary, you may provide placeholder replacements and the preferred language as the first and second arguments to the `translate` method: + + $fail('validation.location')->translate([ + 'value' => $this->value, + ], 'fr') + #### Accessing Additional Data If your custom validation rule class needs to access all of the other data undergoing validation, your rule class may implement the `Illuminate\Contracts\Validation\DataAwareRule` interface. This interface requires your class to define a `setData` method. This method will automatically be invoked by Laravel (before validation proceeds) with all of the data under validation: @@ -1908,10 +2011,10 @@ If your custom validation rule class needs to access all of the other data under namespace App\Rules; - use Illuminate\Contracts\Validation\Rule; use Illuminate\Contracts\Validation\DataAwareRule; + use Illuminate\Contracts\Validation\InvokableRule; - class Uppercase implements Rule, DataAwareRule + class Uppercase implements DataAwareRule, InvokableRule { /** * All of the data under validation. @@ -1942,10 +2045,10 @@ Or, if your validation rule requires access to the validator instance performing namespace App\Rules; - use Illuminate\Contracts\Validation\Rule; + use Illuminate\Contracts\Validation\InvokableRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; - class Uppercase implements Rule, ValidatorAwareRule + class Uppercase implements InvokableRule, ValidatorAwareRule { /** * The validator instance. @@ -2002,12 +2105,11 @@ By default, when an attribute being validated is not present or contains an empt Validator::make($input, $rules)->passes(); // true -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. - -To generate a new implicit rule object, you may use the `make:rule` Artisan command with the `--implicit` option : +For a custom rule to run even when an attribute is empty, the rule must imply that the attribute is required. To quickly generate a new implicit rule object, you may use the `make:rule` Artisan command with the `--implicit` option: ```shell -php artisan make:rule Uppercase --implicit +php artisan make:rule Uppercase --invokable --implicit ``` -> {note} An "implicit" rule only _implies_ that the attribute is required. Whether it actually invalidates a missing or empty attribute is up to you. +> **Warning** +> An "implicit" rule only _implies_ that the attribute is required. Whether it actually invalidates a missing or empty attribute is up to you. diff --git a/verification.md b/verification.md index 9caf5baf084..f56f8ece89d 100644 --- a/verification.md +++ b/verification.md @@ -16,7 +16,8 @@ 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. The starter kits will take care of scaffolding your entire authentication system, including email verification support. +> **Note** +> 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. ### Model Preparation @@ -75,7 +76,8 @@ 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 is 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 application starter kits](/docs/{{version}}/starter-kits). +> **Note** +> 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 @@ -110,11 +112,11 @@ Sometimes a user may misplace or accidentally delete the email address verificat ### Protecting Routes -[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 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. Typically, this middleware is paired with the `auth` middleware: Route::get('/profile', function () { // Only verified users may access this route... - })->middleware('verified'); + })->middleware(['auth', '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). @@ -148,7 +150,8 @@ To get started, pass a closure to the `toMailUsing` method provided by the `Illu }); } -> {tip} To learn more about mail notifications, please consult the [mail notification documentation](/docs/{{version}}/notifications#mail-notifications). +> **Note** +> To learn more about mail notifications, please consult the [mail notification documentation](/docs/{{version}}/notifications#mail-notifications). ## Events diff --git a/views.md b/views.md index 0df245f8164..30b521b836c 100644 --- a/views.md +++ b/views.md @@ -32,7 +32,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. +> **Note** +> 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 @@ -60,7 +61,8 @@ Views may also be nested within subdirectories of the `resources/views` director return view('admin.profile', $data); -> {note} View directory names should not contain the `.` character. +> **Warning** +> View directory names should not contain the `.` character. ### Creating The First Available View @@ -177,7 +179,8 @@ We'll use the `View` facade's `composer` method to register the view composer. L } } -> {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. +> **Warning** +> 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 `compose` method of the `App\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: @@ -266,4 +269,3 @@ You may use the `view:clear` command to clear the view cache: ```shell php artisan view:clear ``` - diff --git a/vite.md b/vite.md new file mode 100644 index 00000000000..31fc0398376 --- /dev/null +++ b/vite.md @@ -0,0 +1,706 @@ +# Asset Bundling (Vite) + +- [Introduction](#introduction) +- [Installation & Setup](#installation) + - [Installing Node](#installing-node) + - [Installing Vite And The Laravel Plugin](#installing-vite-and-laravel-plugin) + - [Configuring Vite](#configuring-vite) + - [Loading Your Scripts And Styles](#loading-your-scripts-and-styles) +- [Running Vite](#running-vite) +- [Working With JavaScript](#working-with-scripts) + - [Aliases](#aliases) + - [Vue](#vue) + - [React](#react) + - [Inertia](#inertia) + - [URL Processing](#url-processing) +- [Working With Stylesheets](#working-with-stylesheets) +- [Working With Blade & Routes](#working-with-blade-and-routes) + - [Processing Static Assets With Vite](#blade-processing-static-assets) + - [Refreshing On Save](#blade-refreshing-on-save) +- [Custom Base URLs](#custom-base-urls) +- [Environment Variables](#environment-variables) +- [Disabling Vite In Tests](#disabling-vite-in-tests) +- [Server-Side Rendering (SSR)](#ssr) +- [Script & Style Tag Attributes](#script-and-style-attributes) + - [Content Security Policy (CSP) Nonce](#content-security-policy-csp-nonce) + - [Subresource Integrity (SRI)](#subresource-integrity-sri) + - [Arbitrary Attributes](#arbitrary-attributes) +- [Advanced Customization](#advanced-customization) + + +## Introduction + +[Vite](https://vitejs.dev) is a modern frontend build tool that provides an extremely fast development environment and bundles your code for production. When building applications with Laravel, you will typically use Vite to bundle your application's CSS and JavaScript files into production ready assets. + +Laravel integrates seamlessly with Vite by providing an official plugin and Blade directive to load your assets for development and production. + +> **Note** +> Are you running Laravel Mix? Vite has replaced Laravel Mix in new Laravel installations. For Mix documentation, please visit the [Laravel Mix](https://laravel-mix.com/) website. If you would like to switch to Vite, please see our [migration guide](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite). + + +#### Choosing Between Vite And Laravel Mix + +Before transitioning to Vite, new Laravel applications utilized [Mix](https://laravel-mix.com/), which is powered by [webpack](https://webpack.js.org/), when bundling assets. Vite focuses on providing a faster and more productive experience when building rich JavaScript applications. If you are developing a Single Page Application (SPA), including those developed with tools like [Inertia](https://inertiajs.com), Vite will be the perfect fit. + +Vite also works well with traditional server-side rendered applications with JavaScript "sprinkles", including those using [Livewire](https://laravel-livewire.com). However, it lacks some features that Laravel Mix supports, such as the ability to copy arbitrary assets into the build that are not referenced directly in your JavaScript application. + + +#### Migrating Back To Mix + +Have you started a new Laravel application using our Vite scaffolding but need to move back to Laravel Mix and webpack? No problem. Please consult our [official guide on migrating from Vite to Mix](https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-vite-to-laravel-mix). + + +## Installation & Setup + +> **Note** +> The following documentation discusses how to manually install and configure the Laravel Vite plugin. However, Laravel's [starter kits](/docs/{{version}}/starter-kits) already include all of this scaffolding and are the fastest way to get started with Laravel and Vite. + + +### Installing Node + +You must ensure that Node.js (16+) and NPM are installed before running Vite and the Laravel plugin: + +```sh +node -v +npm -v +``` + +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](https://laravel.com/docs/{{version}}/sail), you may invoke Node and NPM through Sail: + +```sh +./vendor/bin/sail node -v +./vendor/bin/sail npm -v +``` + + +### Installing Vite And The Laravel Plugin + +Within a fresh installation of Laravel, you will find a `package.json` file in the root of your application's directory structure. The default `package.json` file already includes everything you need to get started using Vite and the Laravel plugin. You may install your application's frontend dependencies via NPM: + +```sh +npm install +``` + + +### Configuring Vite + +Vite is configured via a `vite.config.js` file in the root of your project. You are free to customize this file based on your needs, and you may also install any other plugins your application requires, such as `@vitejs/plugin-vue` or `@vitejs/plugin-react`. + +The Laravel Vite plugin requires you to specify the entry points for your application. These may be JavaScript or CSS files, and include preprocessed languages such as TypeScript, JSX, TSX, and Sass. + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel([ + 'resources/css/app.css', + 'resources/js/app.js', + ]), + ], +}); +``` + +If you are building an SPA, including applications built using Inertia, Vite works best without CSS entry points: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel([ + 'resources/css/app.css', // [tl! remove] + 'resources/js/app.js', + ]), + ], +}); +``` + +Instead, you should import your CSS via JavaScript. Typically, this would be done in your application's `resources/js/app.js` file: + +```js +import './bootstrap'; +import '../css/app.css'; // [tl! add] +``` + +The Laravel plugin also supports multiple entry points and advanced configuration options such as [SSR entry points](#ssr). + + +#### Working With A Secure Development Server + +If your development web server is running on HTTPS, including Valet's [secure command](/docs/{{version}}/valet#securing-sites), you may run into issues connecting to the Vite development server. You may configure Vite to also run on HTTPS by adding the following to your `vite.config.js` configuration file: + +```js +export default defineConfig({ + // ... + server: { // [tl! add] + https: true, // [tl! add] + host: 'localhost', // [tl! add] + }, // [tl! add] +}); +``` + +You will also need to accept the certificate warning for Vite's development server in your browser by following the "Local" link in your console when running the `npm run dev` command. + + +### Loading Your Scripts And Styles + +With your Vite entry points configured, you only need reference them in a `@vite()` Blade directive that you add to the `` of your application's root template: + +```blade + + + {{-- ... --}} + + @vite(['resources/css/app.css', 'resources/js/app.js']) + +``` + +If you're importing your CSS via JavaScript, you only need to include the JavaScript entry point: + +```blade + + + {{-- ... --}} + + @vite('resources/js/app.js') + +``` + +The `@vite` directive will automatically detect the Vite development server and inject the Vite client to enable Hot Module Replacement. In build mode, the directive will load your compiled and versioned assets, including any imported CSS. + +If needed, you may also specify the build path of your compiled assets when invoking the `@vite` directive: + +```blade + + + {{-- Given build path is relative to public path. --}} + + @vite('resources/js/app.js', 'vendor/courier/build') + +``` + + +## Running Vite + +There are two ways you can run Vite. You may run the development server via the `dev` command, which is useful while developing locally. The development server will automatically detect changes to your files and instantly reflect them in any open browser windows. + +Or, running the `build` command will version and bundle your application's assets and get them ready for you to deploy to production: + +```shell +# Run the Vite development server... +npm run dev + +# Build and version the assets for production... +npm run build +``` + + +## Working With JavaScript + + +### Aliases + +By default, The Laravel plugin provides a common alias to help you hit the ground running and conveniently import your application's assets: + +```js +{ + '@' => '/resources/js' +} +``` + +You may overwrite the `'@'` alias by adding your own to the `vite.config.js` configuration file: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel(['resources/ts/app.tsx']), + ], + resolve: { + alias: { + '@': '/resources/ts', + }, + }, +}); +``` + + +### Vue + +There are a few additional options you will need to include in the `vite.config.js` configuration file when using the Vue plugin with the Laravel plugin: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import vue from '@vitejs/plugin-vue'; + +export default defineConfig({ + plugins: [ + laravel(['resources/js/app.js']), + vue({ + template: { + transformAssetUrls: { + // The Vue plugin will re-write asset URLs, when referenced + // in Single File Components, to point to the Laravel web + // server. Setting this to `null` allows the Laravel plugin + // to instead re-write asset URLs to point to the Vite + // server instead. + base: null, + + // The Vue plugin will parse absolute URLs and treat them + // as absolute paths to files on disk. Setting this to + // `false` will leave absolute URLs un-touched so they can + // reference assets in the public directly as expected. + includeAbsolute: false, + }, + }, + }), + ], +}); +``` + +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Vue, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Vue, and Vite. + + +### React + +When using Vite with React, you will need to ensure that any files containing JSX have a `.jsx` or `.tsx` extension, remembering to update your entry point, if required, as [shown above](#configuring-vite). You will also need to include the additional `@viteReactRefresh` Blade directive alongside your existing `@vite` directive. + +```blade +@viteReactRefresh +@vite('resources/js/app.jsx') +``` + +The `@viteReactRefresh` directive must be called before the `@vite` directive. + +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, React, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, React, and Vite. + + +### Inertia + +The Laravel Vite plugin provides a convenient `resolvePageComponent` function to help you resolve your Inertia page components. Below is an example of the helper in use with Vue 3; however, you may also utilize the function in other frameworks such as React: + +```js +import { createApp, h } from 'vue'; +import { createInertiaApp } from '@inertiajs/inertia-vue3'; +import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'; + +createInertiaApp({ + resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')), + setup({ el, App, props, plugin }) { + createApp({ render: () => h(App, props) }) + .use(plugin) + .mount(el) + }, +}); +``` + +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia, and Vite. + + +### URL Processing + +When using Vite and referencing assets in your application's HTML, CSS, or JS, there are a couple of things to consider. First, if you reference assets with an absolute path, Vite will not include the asset in the build; therefore, you should ensure that the asset is available in your public directory. + +When referencing relative asset paths, you should remember that the paths are relative to the file where they are referenced. Any assets referenced via a relative path will be re-written, versioned, and bundled by Vite. + +Consider the following project structure: + +```nothing +public/ + taylor.png +resources/ + js/ + Pages/ + Welcome.vue + images/ + abigail.png +``` + +The following example demonstrates how Vite will treat relative and absolute URLs: + +```html + + + + + +``` + + +## Working With Stylesheets + +You can learn more about Vite's CSS support within the [Vite documentation](https://vitejs.dev/guide/features.html#css). If you are using PostCSS plugins such as [Tailwind](https://tailwindcss.com), you may create a `postcss.config.js` file in the root of your project and Vite will automatically apply it: + +```js +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; +``` + + +## Working With Blade & Routes + + +### Processing Static Assets With Vite + +When referencing assets in your JavaScript or CSS, Vite automatically processes and versions them. In addition, when building Blade based applications, Vite can also process and version static assets that you reference solely in Blade templates. + +However, in order to accomplish this, you need to make Vite aware of your assets by importing the static assets into the application's entry point. For example, if you want to process and version all images stored in `resources/images` and all fonts stored in `resources/fonts`, you should add the following in your application's `resources/js/app.js` entry point: + +```js +import.meta.glob([ + '../images/**', + '../fonts/**', +]); +``` + +These assets will now be processed by Vite when running `npm run build`. You can then reference these assets in Blade templates using the `Vite::asset` method, which will return the versioned URL for a given asset: + +```blade + +``` + + +### Refreshing On Save + +When your application is built using traditional server-side rendering with Blade, Vite can improve your development workflow by automatically refreshing the browser when you make changes to view files in your application. To get started, you can simply specify the `refresh` option as `true`. + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + refresh: true, + }), + ], +}); +``` + +When the `refresh` option is `true`, saving files in `resources/views/**`, `app/View/Components/**`, and `routes/**` will trigger the browser to perform a full page refresh while you are running `npm run dev`. + +Watching the `routes/**` directory is useful if you are utilizing [Ziggy](https://github.com/tighten/ziggy) to generate route links within your application's frontend. + +If these default paths do not suit your needs, you can specify your own list of paths to watch: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + refresh: ['resources/views/**'], + }), + ], +}); +``` + +Under the hood, the Laravel Vite plugin uses the [`vite-plugin-full-reload`](https://github.com/ElMassimo/vite-plugin-full-reload) package, which offers some advanced configuration options to fine-tune this feature's behavior. If you need this level of customization, you may provide a `config` definition: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + // ... + refresh: [{ + paths: ['path/to/watch/**'], + config: { delay: 300 } + }], + }), + ], +}); +``` + + +## Custom Base URLs + +If your Vite compiled assets are deployed to a domain separate from your application, such as via a CDN, you must specify the `ASSET_URL` environment variable within your application's `.env` file: + +```env +ASSET_URL=https://cdn.example.com +``` + +After configuring the asset URL, all re-written URLs to your assets will be prefixed with the configured value: + +```nothing +https://cdn.example.com/build/assets/app.9dce8d17.js +``` + +Remember that [absolute URLs are not re-written by Vite](#url-processing), so they will not be prefixed. + + +## Environment Variables + +You may inject environment variables into your JavaScript by prefixing them with `VITE_` in your application's `.env` file: + +```env +VITE_SENTRY_DSN_PUBLIC=http://example.com +``` + +You may access injected environment variables via the `import.meta.env` object: + +```js +import.meta.env.VITE_SENTRY_DSN_PUBLIC +``` + + +## Disabling Vite In Tests + +Laravel's Vite integration will attempt to resolve your assets while running your tests, which requires you to either run the Vite development server or build your assets. + +If you would prefer to mock Vite during testing, you may call the `withoutVite` method, which is is available for any tests that extend Laravel's `TestCase` class: + +```php +use Tests\TestCase; + +class ExampleTest extends TestCase +{ + public function test_without_vite_example() + { + $this->withoutVite(); + + // ... + } +} +``` + +If you would like to disable Vite for all tests, you may call the `withoutVite` method from the `setUp` method on your base `TestCase` class: + +```php +withoutVite(); + }// [tl! add:end] +} +``` + + +## Server-Side Rendering (SSR) + +The Laravel Vite plugin makes it painless to set up server-side rendering with Vite. To get started, create an SSR entry point at `resources/js/ssr.js` and specify the entry point by passing a configuration option to the Laravel plugin: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: 'resources/js/app.js', + ssr: 'resources/js/ssr.js', + }), + ], +}); +``` + +To ensure you don't forget to rebuild the SSR entry point, we recommend augmenting the "build" script in your application's `package.json` to create your SSR build: + +```json +"scripts": { + "dev": "vite", + "build": "vite build" // [tl! remove] + "build": "vite build && vite build --ssr" // [tl! add] +} +``` + +Then, to build and start the SSR server, you may run the following commands: + +```sh +npm run build +node bootstrap/ssr/ssr.mjs +``` + +> **Note** +> Laravel's [starter kits](/docs/{{version}}/starter-kits) already include the proper Laravel, Inertia SSR, and Vite configuration. Check out [Laravel Breeze](/docs/{{version}}/starter-kits#breeze-and-inertia) for the fastest way to get started with Laravel, Inertia SSR, and Vite. + + +## Script & Style Tag Attributes + + +### Content Security Policy (CSP) Nonce + +If you wish to include a [`nonce` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) on your script and style tags as part of your [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), you may generate or specify a nonce using the `useCspNonce` method within a custom [middleware](/docs/{{version}}/middleware): + +```php +withHeaders([ + 'Content-Security-Policy' => "script-src 'nonce-".Vite::cspNonce()."'", + ]); + } +} +``` + +After invoking the `useCspNonce` method, Laravel will automatically include the `nonce` attributes on all generated script and style tags. + +If you need to specify the nonce elsewhere, including the [Ziggy `@route` directive](https://github.com/tighten/ziggy#using-routes-with-a-content-security-policy) included with Laravel's [starter kits](/docs/{{version}}/starter-kits), you may retrieve it using the `cspNonce` method: + +```blade +@routes(nonce: Vite::cspNonce()) +``` + +If you already have a nonce that you would like to instruct Laravel to use, you may pass the nonce to the `useCspNonce` method: + +```php +Vite::useCspNonce($nonce); +``` + + +### Subresource Integrity (SRI) + +If your Vite manifest includes `integrity` hashes for your assets, Laravel will automatically add the `integrity` attribute on any script and style tags it generates in order to enforce [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). By default, Vite does not include the `integrity` hash in its manifest, but you may enable it by installing the [`vite-plugin-manifest-uri`](https://www.npmjs.com/package/vite-plugin-manifest-sri) NPM plugin: + +```shell +npm install -D vite-plugin-manifest-sri +``` + +You may then enable this plugin in your `vite.config.js` file: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import manifestSRI from 'vite-plugin-manifest-sri';// [tl! add] + +export default defineConfig({ + plugins: [ + laravel({ + // ... + }), + manifestSRI(),// [tl! add] + ], +}); +``` + +If required, you may also customize the manifest key where the integrity hash can be found: + +```php +use Illuminate\Support\Facades\Vite; + +Vite::useIntegrityKey('custom-integrity-key'); +``` + +If you would like to disable this auto-detection completely, you may pass `false` to the `useIntegrityKey` method: + +```php +Vite::useIntegrityKey(false); +``` + + +### Arbitrary Attributes + +If you need to include additional attributes on your script and style tags, such as the [`data-turbo-track`](https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change) attribute, you may specify them via the `useScriptTagAttributes` and `useStyleTagAttributes` methods. Typically, this methods should be invoked from a [service provider](/docs/{{version}}/providers): + +```php +use Illuminate\Support\Facades\Vite; + +Vite::useScriptTagAttributes([ + 'data-turbo-track' => 'reload', // Specify a value for the attribute... + 'async' => true, // Specify an attribute without a value... + 'integrity' => false, // Exclude an attribute that would otherwise be included... +]); + +Vite::useStyleTagAttributes([ + 'data-turbo-track' => 'reload', +]); +``` + +If you need to conditionally add attributes, you may pass a callback that will receive the asset source path, its URL, its manifest chunk, and the entire manifest: + +```php +use Illuminate\Support\Facades\Vite; + +Vite::useScriptTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [ + 'data-turbo-track' => $src === 'resources/js/app.js' ? 'reload' : false, +]); + +Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [ + 'data-turbo-track' => $chunk && $chunk['isEntry'] ? 'reload' : false, +]); +``` + +> **Warning** +> The `$chunk` and `$manifest` arguments will be `null` while the Vite development server is running. + + +## Advanced Customization + +Out of the box, Laravel's Vite plugin uses sensible conventions that should work for the majority of applications; however, sometimes you may need to customize Vite's behavior. To enable additional customization options, we offer the following methods and options which can be used in place of the `@vite` Blade directive: + +```blade + + + {{-- ... --}} + + {{ + Vite::useHotFile(storage_path('vite.hot')) // Customize the "hot" file... + ->useBuildDirectory('bundle') // Customize the build directory... + ->withEntryPoints(['resources/js/app.js']) // Specify the entry points... + }} + +``` + +Within the `vite.config.js` file, you should then specify the same configuration: + +```js +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + hotFile: 'storage/vite.hot', // Customize the "hot" file... + buildDirectory: 'bundle', // Customize the build directory... + input: ['resources/js/app.js'], // Specify the entry points... + }), + ], +}); +```