lpineJS

Livewire 3 ships with [AlpineJS](https://alpinejs.dev) by default.

If you manually include Alpine in your Livewire application, you will need to remove it, so that Livewire's built-in version doesn't conflict.

### Including Alpine via a script tag

If you include Alpine into your application via a script tag like the following, you can remove it entirely and Livewire will load its internal version instead:

```html
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <!-- [tl! remove] -->
```

### Including plugins via a script tag

Livewire 3 now ships with the following Alpine plugins out-of-the-box:

* [Anchor](https://alpinejs.dev/plugins/anchor)
* [Collapse](https://alpinejs.dev/plugins/collapse)
* [Focus](https://alpinejs.dev/plugins/focus)
* [Intersect](https://alpinejs.dev/plugins/intersect)
* [Mask](https://alpinejs.dev/plugins/mask)
* [Morph](https://alpinejs.dev/plugins/morph)
* [Persist](https://alpinejs.dev/plugins/persist)

It is worth keeping an eye on changes to the [package.json](https://github.com/livewire/livewire/blob/main/package.json) file, as new Alpine plugins may be added!

If you have previously included any of these in your application via `<script>` tags like below, you should remove them along with Alpine's core:

```html
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/intersect@3.x.x/dist/cdn.min.js"></script> <!-- [tl! remove:1] -->
<!-- ... -->
```

### Accessing the Alpine global via a script tag

If you are currently accessing the `Alpine` global object from a script tag like so:

```html
<script>
    document.addEventListener('alpine:init', () => {
        Alpine.data(...)
    })
</script>
```

You may continue to do so, as Livewire internally includes and registers Alpine's global object like before.

### Including via JS bundle

If you have included Alpine or any of the popular core Alpine plugins mentioned above via NPM into your applications JavaScript bundle like so:

```js
// Warning: this is a snippet of the Livewire 2 approach to including Alpine

import Alpine from 'alpinejs'
import intersect from '@alpinejs/intersect'

Alpine.plugin(intersect)

Alpine.start()
```

You can remove them entirely, because Livewire includes Alpine and many popular Alpine plugins by default.

#### Accessing Alpine via JS bundle

If you are registering custom Alpine plugins or components inside your application's JavaScript bundle like so:

```js
// Warning: this is a snippet of the Livewire 2 approach to including Alpine

import Alpine from 'alpinejs'
import customPlugin from './plugins/custom-plugin'

Alpine.plugin(customPlugin)

Alpine.start()
```

You can still accomplish this by importing the Livewire core ESM module into your bundle and accessing `Alpine` from there.

To import Livewire into your bundle, you must first disable Livewire's normal JavaScript injection and provide the necessary configuration to Livewire by replacing `@livewireScripts` with `@livewireScriptConfig` in your application's primary layout:

```blade
    <!-- ... -->

    @livewireScripts <!-- [tl! remove] -->
    @livewireScriptConfig <!-- [tl! add] -->
</body>
```

Now, you can import `Alpine` and `Livewire` into your application's bundle like so:

```js
import { Livewire, Alpine } from '../../vendor/livewire/livewire/dist/livewire.esm';
import customPlugin from './plugins/custom-plugin'

Alpine.plugin(customPlugin)

Livewire.start()
```

Notice you no longer need to call `Alpine.start()`. Livewire will start Alpine automatically.

For more information, please consult our documentation on [manually bundling Livewire's JavaScript](/docs/installation#manually-bundling-livewire-and-alpine).

## `wire:model`

In Livewire 3, `wire:model` is "deferred" by default (instead of by `wire:model.defer`). To achieve the same behavior as `wire:model` from Livewire 2, you must use `wire:model.live`.

Below is a list of the necessary substitutions you will need to make in your templates to keep your application's behavior consistent:

```html
<input wire:model="..."> <!-- [tl! remove] -->
<input wire:model.live="..."> <!-- [tl! add] -->

<input wire:model.defer="..."> <!-- [tl! remove] -->
<input wire:model="..."> <!-- [tl! add] -->

<input wire:model.lazy="..."> <!-- [tl! remove] -->
<input wire:model.blur="..."> <!-- [tl! add] -->
```

## `@entangle`

Similar to the changes to `wire:model`, Livewire 3 defers all data binding by default. To match this behavior, `@entangle` has been updated as well.

To keep your application running as expected, make the following `@entangle` substitutions:

```blade
@entangle(...) <!-- [tl! remove] -->
@entangle(...).live <!-- [tl! add] -->

@entangle(...).defer <!-- [tl! remove] -->
@entangle(...) <!-- [tl! add] -->
```

## E


Livewire is a Laravel package, so you will need to have a Laravel application up and running before you can install and use Livewire. If you need help setting up a new Laravel application, please see the [official Laravel documentation](https://laravel.com/docs/installation).

To install Livewire, open your terminal and navigate to your Laravel application directory, then run the following command:

```shell
composer require livewire/livewire
```

That's it — really. If you want more customization options, keep reading. Otherwise, you can jump right into using Livewire.

> [!warning] `/livewire/livewire.js` returning a 404 status code
> By default, Livewire exposes a route in your application to serve its JavaScript assets from: `/livewire/livewire.js`. This is fine for most applications, however, if you are using Nginx with a custom configuration, you may receive a 404 from this endpoint. To fix this issue, you can either [compile Livewire's JavaScript assets yourself](#manually-bundling-livewire-and-alpine), or [configure Nginx to allow for this](https://benjamincrozat.com/livewire-js-404-not-found).

## Publishing the configuration file

Livewire is "zero-config", meaning you can use it by following conventions, without any additional configuration. However, if needed, you can publish and customize Livewire's configuration file by running the following Artisan command:

```shell
php artisan livewire:publish --config
```

This will create a new `livewire.php` file in your Laravel application's `config` directory.

## Manually including Livewire's frontend assets

By default, Livewire injects the JavaScript and CSS assets it needs into each page that includes a Livewire component.

If you want more control over this behavior, you can manually include the assets on a page using the following Blade directives:

```blade
<html>
<head>
	...
	@livewireStyles
</head>
<body>
	...
	@livewireScripts
</body>
</html>
```

By including these assets manually on a page, Livewire knows not to inject the assets automatically.

> [!warning] AlpineJS is bundled with Livewire
> Because Alpine is bundled with Livewire's JavaScript assets, you must include @verbatim`@livewireScripts`@endverbatim on every page you wish to use Alpine. Even if you're not using Livewire on that page.

Though rarely required, you may disable Livewire's auto-injecting asset behavior by updating the `inject_assets` [configuration option](#publishing-the-configuration-file) in your application's `config/livewire.php` file:

```php
'inject_assets' => false,
```

If you'd rather force Livewire to inject its assets on a single page or multiple pages, you can call the following global method from the current route or from a service provider.

```php
\Livewire\Livewire::forceAssetInjection();
```

## Configuring Livewire's update endpoint

Every update in a Livewire component sends a network request to the server at the following endpoint: `https://example.com/livewire/update`

This can be a problem for some applications that use localization or multi-tenancy.

In those cases, you can register your own endpoint however you like, and as long as you do it inside `Livewire::setUpdateRoute()`,  Livewire will know to use this endpoint for all component updates:

```php
Livewire::setUpdateRoute(function ($handle) {
	return Route::post('/custom/livewire/update', $handle);
});
```

Now, instead of using `/livewire/update`, Livewire will send component updates to `/custom/livewire/update`.

Because Livewire allows you to register your own update route, you can declare any additional middleware you want Livewire to use directly inside `setUpdateRoute()`:

```php
Livewire::setUpdateRoute(function ($handle) {
	return Route::post('/custom/livewire/update', $handle)
        ->middleware([...]); // [tl! highlight]
});
```

## Customizing the asset URL

By default, Livewire will serve its JavaScript assets from the following URL: `https://example.com/livewire/livewire.js`. Additionally, Livewire will reference this asset from a script tag like so:

```blade
<script src="/livewire/livewire.js" ...
```

If your application has global route prefixes due to localization or multi-tenancy, you can register your own endpoint that Livewire should use internally when fetching its JavaScript.

To use a custom JavaScript asset endpoint, you can register your own route inside `Livewire::setScriptRoute()`:

```php
Livewire::setScriptRoute(function ($handle) {
    return Route::get('/custom/livewire/livewire.js', $handle);
});
```

Now, Livewire will load its JavaScript like so:

```blade
<script src="/custom/livewire/livewire.js" ...
```

## Manually bundling Livewire and Alpine

By default, Alpine and Livewire are loaded using the `<script src="livewire.js">` tag, which means you have no control over the order in which these libraries are loaded. Consequently, importing and registering Alpine plugins, as shown in the example below, will no longer function:

```js
// Warning: This snippet demonstrates what NOT to do...

import Alpine from 'alpinejs'
import Clipboard from '@ryangjchandler/alpine-clipboard'

Alpine.plugin(Clipboard)
Alpine.start()
```

To address this issue, we need to inform Livewire that we want to use the ESM (ECMAScript module) version ourselves and prevent the injection of the `livewire.js` script tag. To achieve this, we must add the `@livewireScriptConfig` directive to our layout file (`resources/views/components/layouts/app.blade.php`):

```blade
<html>
<head>
    <!-- ... -->
    @livewireStyles
    @vite(['resources/js/app.js'])
</head>
<body>
    {{ $slot }}

    @livewireScriptConfig <!-- [tl! highlight] -->
</body>
</html>
```

When Livewire detects the `@livewireScriptConfig` directive, it will refrain from injecting the Livewire and Alpine scripts. If you are using the `@livewireScripts` directive to manually load Livewire, be sure to remove it. Make sure to add the `@livewireStyles` directive if it is not already present.

The final step is importing Alpine and Livewire in our `app.js` file, allowing us to register any custom resources, and ultimately starting Livewire and Alpine:

```js
import { Livewire, Alpine } from '../../vendor/livewire/livewire/dist/livewire.esm';
import Clipboard from '@ryangjchandler/alpine-clipboard'

Alpine.plugin(Clipboard)

Livewire.start()
```

> [!tip] Rebuild your assets after composer update
> Make sure that if you are manually bundling Livewire and Alpine, that you rebuild your assets whenever you run `composer update`.

> [!warning] Not compatible with Laravel Mix
> Laravel Mix will not work if you are manually bundling Livewire and AlpineJS. Instead, we recommend that you [switch to Vite](https://laravel.com/docs/vite).

## Publishing Livewire's frontend assets

> [!warning] Publishing assets isn't necessary
> Publishing Livewire's assets isn't necessary for Livewire to run. Only do this if you have a specific need for it.

If you prefer the JavaScript assets to be served by your web server not through Laravel, use the `livewire:publish` command:

```bash
php artisan livewire:publish --assets
```

To keep assets up-to-date and avoid issues in future updates, we strongly recommend that you add the following command to your composer.json file:

```json
{
    "scripts": {
        "post-update-cmd": [
            // Other scripts
            "@php artisan vendor:publish --tag=livewire:assets --ansi --force"
        ]
    }
}
```


It's important to make sure your Livewire apps are secure and don't expose any application vulnerabilities. Livewire has internal security features to handle many cases, however, there are times when it's up to your application code to keep your components secure.

## Authorizing action parameters

Livewire actions are extremely powerful, however, any parameters passed to Livewire actions are mutable on the client and should be treated as un-trusted user input.

Arguably the most common security pitfall in Livewire is failing to validate and authorize Livewire action calls before persisting changes to the database.

Here is an example of an insecurity resulting from a lack of authorization:

```php
<?php

use App\Models\Post;
use Livewire\Component;

class ShowPost extends Component
{
    // ...

    public function delete($id)
    {
        // INSECURE!

        $post = Post::find($id);

        $post->delete();
    }
}
```

```html
<button wire:click="delete({{ $post->id }})">Delete Post</button>
```

The reason the above example is insecure is that `wire:click="delete(...)"` can be modified in the browser to pass ANY post ID a malicious user wishes.

Action parameters (like `$id` in this case) should be treated the same as any untrusted input from the browser.

Therefore, to keep this application secure and prevent a user from deleting another user's post, we must add authorization to the `delete()` action.

First, let's create a [Laravel Policy](https://laravel.com/docs/authorization#creating-policies) for the Post model by running the following command:

```bash
php artisan make:policy PostPolicy --model=Post
```

After running the above command, a new Policy will be created inside `app/Policies/PostPolicy.php`. We can then update its contents with a `delete` method like so:

```php
<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    /**
     * Determine if the given post can be deleted by the user.
     */
    public function delete(?User $user, Post $post): bool
    {
        return $user?->id === $post->user_id;
    }
}
```

Now, we can use the `$this->authorize()` method from the Livewire component to ensure the user owns the post before deleting it:

```php
public function delete($id)
{
    $post = Post::find($id);

    // If the user doesn't own the post,
    // an AuthorizationException will be thrown...
    $this->authorize('delete', $post); // [tl! highlight]

    $post->delete();
}
```

Further reading:
* [Laravel Gates](https://laravel.com/docs/authorization#gates)
* [Laravel Policies](https://laravel.com/docs/authorization#creating-policies)

## Authorizing public properties

Similar to action parameters, public properties in Livewire should be treated as un-trusted input from the user.

Here is the same example from above about deleting a post, written insecurely in a different manner:

```php
<?php

use App\Models\Post;
use Livewire\Component;

class ShowPost extends Component
{
    public $postId;

    public function mount($postId)
    {
        $this->postId = $postId;
    }

    public function delete()
    {
        // INSECURE!

        $post = Post::find($this->postId);

        $post->delete();
    }
}
```

```html
<button wire:click="delete">Delete Post</button>
```

As you can see, instead of passing the `$postId` as a parameter to the `delete` method from `wire:click`, we are storing it as a public property on the Livewire component.

The problem with this approach is that any malicious user can inject a custom element onto the page such as:

```html
<input type="text" wire:model="postId">
```

This would allow them to freely modify the `$postId` before pressing "Delete Post". Because the `delete` action doesn't authorize the value of `$postId`, the user can now delete any post in the database, whether they own it or not.

To protect against this risk, there are two possible solutions:

### Using model properties

When setting public properties, Livewire treats models differently than plain values such as strings and integers. Because of this, if we instead store the entire post model as a property on the component, Livewire will ensure the ID is never tampered with.

Here is an example of storing a `$post` property instead of a simple `$postId` property:

```php
<?php

use App\Models\Post;
use Livewire\Component;

class ShowPost extends Component
{
    public Post $post;

    public function mount($postId)
    {
        $this->post = Post::find($postId);
    }

    public function delete()
    {
        $this->post->delete();
    }
}
```

```html
<button wire:click="delete">Delete Post</button>
```

This component is now secured because there is no way for a malicious user to change the `$post` property to a different Eloquent model.

### Locking the property
Another way to prevent properties from being set to unwanted values is to use [locked properties](https://livewire.laravel.com/docs/locked). Locking properties is done by applying the `#[Locked]` attribute. Now if users attempt to tamper with this value an error will be thrown.

Note that properties with the Locked attribute can still be changed in the back-end, so care still needs to taken that untrusted user input is not passed to the property in your own Livewire functions.

```php
<?php

use App\Models\Post;
use Livewire\Component;
use Livewire\Attributes\Locked;

class ShowPost extends Component
{
    #[Locked] // [tl! highlight]
    public $postId;

    public function mount($postId)
    {
        $this->postId = $postId;
    }

    public function delete()
    {
        $post = Post::find($this->postId);

        $post->delete();
    }
}
```

### Authorizing the property

If using a model property is undesired in your scenario, you can of course fall-back to manually authorizing the deletion of the post inside the `delete` action:

```php
<?php

use App\Models\Post;
use Livewire\Component;

class ShowPost extends Component
{
    public $postId;

    public function mount($postId)
    {
        $this->postId = $postId;
    }

    public function delete()
    {
        $post = Post::find($this->postId);

        $this->authorize('delete', $post); // [tl! highlight]

        $post->delete();
    }
}
```

```html
<button wire:click="delete">Delete Post</button>
```

Now, even though a malicious user can still freely modify the value of `$postId`, when the `delete` action is called, `$this->authorize()` will throw an `AuthorizationException` if the user does not own the post.

Further reading:
* [Laravel Gates](https://laravel.com/docs/authorization#gates)
* [Laravel Policies](https://laravel.com/docs/authorization#creating-policies)


## Middleware

When a Livewire component is loaded on a page containing route-level [Authorization Middleware](https://laravel.com/docs/authorization#via-middleware), like so:

```php
Route::get('/post/{post}', App\Livewire\UpdatePost::class)
    ->middleware('can:update,post'); // [tl! highlight]
```

Livewire will ensure those middlewares are re-applied to subsequent Livewire network requests. This is referred to as "Persistent Middleware" in Livewire's core.

Persistent middleware protects you from scenarios where the authorization rules or user permissions have changed after the initial page-load.

Here's a more in-depth example of such a scenario:

```php
Route::get('/post/{post}', App\Livewire\UpdatePost::class)
    ->middleware('can:update,post'); // [tl! highlight]
```

```php
<?php

use App\Models\Post;
use Livewire\Component;
use Livewire\Attributes\Validate;

class UpdatePost extends Component
{
    public Post $post;

    #[Validate('required|min:5')]
    public $title = '';

    public $content = '';

    public function mount()
    {
        $this->title = $this->post->title;
        $this->content = $this->post->content;
    }

    public function update()
    {
        $this->post->update([
            'title' => $this->title,
            'content' => $this->content,
        ]);
    }
}
```

As you can see, the `can:update,post` middleware is applied at the route-level. This means that a user who doesn't have permission to update a post cannot view the page.

However, consider a scenario where a user:
* Loads the page
* Loses permission to update after the page loads
* Tries updating the post after losing permission

Because Livewire has already successfully loaded the page you might ask yourself: "When Livewire makes a subsequent request to update the post, will the `can:update,post` middleware be re-applied? Or instead, will the un-authorized user be able to update the post successfully?"

Because Livewire has internal mechanisms to re-apply middleware from the original endpoint, you are protected in this scenario.

### Configuring persistent middleware

By default, Livewire persists the following middleware across network requests:

```php
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Laravel\Jetstream\Http\Middleware\AuthenticateSession::class,
\Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\RedirectIfAuthenticated::class,
\Illuminate\Auth\Middleware\Authenticate::class,
\Illuminate\Auth\Middleware\Authorize::class,
```

If any of the above middlewares are applied to the initial page-load, they will be persisted (re-applied) to any future network requests.

However, if you are applying a custom middleware from your application the initial page-load, and want it persisted between Livewire requests, you will need to add it to this list from a [Service Provider](https://laravel.com/docs/providers#main-content) in your app like so:

```php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Livewire;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Livewire::addPersistentMiddleware([ // [tl! highlight:2]
            App\Http\Middleware\EnsureUserHasRole::class,
        ]);
    }
}
```

If a Livewire component is loaded on a page that uses the `EnsureUserHasRole` middleware from your application, it will now be persisted and re-applied to any future network requests to that Livewire component.

> [!warning] Middleware arguments are not supported
> Livewire currently doesn't support middleware arguments for persistent middleware definitions.
>
> ```php
> // Bad...
> Livewire::addPersistentMiddleware(AuthorizeResource::class.':admin');
>
> // Good...
> Livewire::addPersistentMiddleware(AuthorizeResource::class);
> ```

### Applying global Livewire middleware

Alternatively, if you wish to apply specific middleware to every single Livewire update network request, you can do so by registering your own Livewire update route with any middleware you wish:

```php
Livewire::setUpdateRoute(function ($handle) {
	return Route::post('/livewire/update', $handle)
        ->middleware(App\Http\Middleware\LocalizeViewPaths::class);
});
```

Any Livewire AJAX/fetch requests made to the server will use the above endpoint and apply the `LocalizeViewPaths` middleware before handling the component update.

Learn more about [customizing the update route on the Installation page](https://livewire.laravel.com/docs/installation#configuring-livewires-update-endpoint).

## Snapshot checksums

Between every Livewire request, a snapshot is taken of the Livewire component and sent to the browser. This snapshot is used to re-build the component during the next server round-trip.

[Learn more about Livewire snapshots in the Hydration documentation.](https://livewire.laravel.com/docs/hydration#the-snapshot)

Because fetch requests can be intercepted and tampered with in a browser, Livewire generates a "checksum" of each snapshot to go along with it.

This checksum is then used on the next network request to verify that the snapshot hasn't changed in any way.

If Livewire finds a checksum mismatch, it will throw a `CorruptComponentPayloadException` and the request will fail.

This protects against any form of malicious tampering that would otherwise result in granting users the ability to execute or modify unrelated code.


class HttpKernel extends Kernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \Illuminate\Foundation\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \Illuminate\Cookie\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \Orchestra\Testbench\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];

    /**
     * The priority-sorted list of middleware.
     *
     * This forces non-global middleware to always be in the given order.
     *
     * @var array
     */
    protected $middlewarePriority = [
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Auth\Middleware\Authenticate::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];
}

class Component extends BaseComponent
{
    public static $loggedMiddleware = [];

    public $middleware = [];
    public $showNested = false;
    public $changeProtected = false;

    public function mount(Post $post)
    {
        //
    }

    public function showNestedComponent()
    {
        $this->showNested = true;
    }

    public function toggleProtected()
    {
        $this->changeProtected = ! $this->changeProtected;
    }

    public function render()
    {
        $this->middleware = static::$loggedMiddleware;

        return <<<'HTML'

    @json($middleware)

    <button wire:click="$refresh" dusk="refresh">Refresh</button>
    <button wire:click="toggleProtected" dusk="changeProtected">Change Protected</button>
    <button wire:click="showNestedComponent" dusk="showNested">Show Nested</button>

        @unless($changeProtected)
            Protected Content
        @else
            Still Secure Content
        @endunless

    @if ($showNested)
        @livewire(\Livewire\Mechanisms\PersistentMiddleware\NestedComponent::class)
    @endif

HTML;
    }
}

class NestedComponent extends BaseComponent
{
    public $middleware = [];

    public function render()
    {
        $this->middleware = Component::$loggedMiddleware;

        return <<<'HTML'

    @json($middleware)

    <button wire:click="$refresh" dusk="refreshNested">Refresh</button>

HTML;
    }
}

class AllowListedMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        Component::$loggedMiddleware[] = static::class;

        return $next($request);
    }
}

class BlockListedMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        Component::$loggedMiddleware[] = static::class;

        return $next($request);
    }
}

class User extends AuthUser
{
    use Sushi;

    protected $fillable = ['banned'];

    public function posts()
    {
        return $this->hasMany(Post::class);
    }

    protected $rows = [
        [
            'id' => 1,
            'name' => 'First User',
            'email' => 'first@laravel-livewire.com',
            'password' => '',
            'banned' => false,
        ],
        [
            'id' => 2,
            'name' => 'Second user',
            'email' => 'second@laravel-livewire.com',
            'password' => '',
            'banned' => false,
        ],
    ];
}

class Post extends Model
{
    use Sushi;

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    protected $rows = [
        ['title' => 'First', 'user_id' => 1],
        ['title' => 'Second', 'user_id' => 2],
    ];
}

class PostPolicy
{
    public function update(User $user, Post $post)
    {
        return (int) $post->user_id === (int) $user->id;
    }
}

class IsBanned
{
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->user()->banned) {
            return redirect('/force-logout');
        }

        return $next($request);
    }
}


<?php

namespace Livewire\Features\SupportLegacyModels;

use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Livewire\Mechanisms\HandleComponents\Synthesizers\Synth;
use LogicException;

class EloquentCollectionSynth extends Synth
{
    public static $key = 'elcl';

    public static function match($target)
    {
        return $target instanceof EloquentCollection;
    }

    public function dehydrate(EloquentCollection $target, $dehydrateChild)
    {
        $class = $target::class;
        $modelClass = $target->getQueueableClass();

        $meta = [];

        $meta['keys'] = $target->modelKeys();
        $meta['class'] = $class;
        $meta['modelClass'] = $modelClass;

        if ($modelClass && ($connection = $this->getConnection($target)) !== $modelClass::make()->getConnectionName()) {
            $meta['connection'] = $connection;
        }

        $relations = $target->getQueueableRelations();

        if (count($relations)) {
            $meta['relations'] = $relations;
        }

        $rules = $this->getRules($this->context);

        if (empty($rules)) return [[], $meta];

        $data = $this->getDataFromCollection($target, $rules);

        foreach ($data as $key => $child) {

            $data[$key] = $dehydrateChild($key, $child);
        }

        return [ $data, $meta ];
    }

    public function hydrate($data, $meta, $hydrateChild)
    {
        if (isset($meta['__child_from_parent'])) {
            $collection = $meta['__child_from_parent'];

            unset($meta['__child_from_parent']);
        } else {
            $collection = $this->loadCollection($meta);
        }

        if (isset($meta['relations'])) {
            $collection->loadMissing($meta['relations']);
        }

        if (count($data)) {
            foreach ($data as $key => $childData) {
                $childData[1]['__child_from_parent'] = $collection->get($key);

                $data[$key] = $hydrateChild($key, $childData);
            }

            return $collection::wrap($data);
        }

        return $collection;
    }

    public function get(&$target, $key)
    {
        return $target->get($key);
    }

    public function set(&$target, $key, $value, $pathThusFar, $fullPath)
    {
        if (SupportLegacyModels::missingRuleFor($this->context->component, $fullPath)) {
            throw new CannotBindToModelDataWithoutValidationRuleException($fullPath, $this->context->component->getName());
        }

        $target->put($key, $value);
    }

    public function methods($target)
    {
        return [];
    }

    public function call($target, $method, $params, $addEffect)
    {
    }

    protected function getRules($context)
    {
        $key = $this->path ?? null;

        if (is_null($key)) return [];

        return SupportLegacyModels::getRulesFor($context->component, $key);
    }

    protected function getConnection(EloquentCollection $collection)
    {
        if ($collection->isEmpty()) {
            return;
        }

        $connection = $collection->first()->getConnectionName();

        $collection->each(function ($model) use ($connection) {
            // If there is no connection name, it must be a new model so continue.
            if (is_null($model->getConnectionName())) {
                return;
            }

            if ($model->getConnectionName() !== $connection) {
                throw new LogicException('Livewire can\'t dehydrate an Eloquent Collection with models from different connections.');
            }
        });

        return $connection;
    }

    protected function getDataFromCollection(EloquentCollection $collection, $rules)
    {
        return $this->filterData($collection->all(), $rules);
    }

    protected function filterData($data, $rules)
    {
        return array_filter($data, function ($key) use ($rules) {
            return array_key_exists('*', $rules);
        }, ARRAY_FILTER_USE_KEY);
    }

    protected function loadCollection($meta)
    {
        if (isset($meta['keys']) && count($meta['keys']) >= 0 && ! empty($meta['modelClass'])) {
            $model = new $meta['modelClass'];

            if (isset($meta['connection'])) {
                $model->setConnection($meta['connection']);
            }

            $query = $model->newQueryForRestoration($meta['keys']);

            if (isset($meta['relations'])) {
                $query->with($meta['relations']);
            }

            $query->useWritePdo();

            $collection = $query->get();

            $collection = $collection->keyBy->getKey();

            return new $meta['class'](
                collect($meta['keys'])->map(function ($id) use ($collection) {
                    return $collection[$id] ?? null;
                })->filter()
            );
        }

        return new $meta['class']();
    }
}


public function test_it_serialises_properties_from_model_that_has_not_been_persisted()
    {
        // @todo: Review this, as it's not quite correct, key "name" should be sent to the front end, even if not set, to match V2 functionality
        $model = Author::make();

        $rules = [
            'model.name' => '',
        ];

        $expected = [
            'name' => null,
        ];

        $component = Livewire::test(ModelsComponent::class, ['model' => $model, 'rules' => $rules]);

        $results = $component->snapshot['data']['model'][0];

        $this->assertEquals($expected['name'], $results['name']);
    }

    public function test_it_ignores_the_key_if_the_model_does_not_exist()
    {
        $this->expectNotToPerformAssertions();

        $model = Author::make();

        $model->id = 123;

        Livewire::test(ModelsComponent::class, ['model' => $model])
            ->call('$refresh');
    }
}

class PostComponent extends TestComponent
{
    public $post;

    public function mount()
    {
        $this->post = Post::first();
    }
}

class ModelsComponent extends TestComponent
{
    public $model;
    public $models;

    public $_rules = [
        'model.title' => '',
        'model.name' => '',
        'model.email' => '',
        'model.posts.*.title' => '',
        'model.posts.*.description' => '',
        'model.posts.*.content' => '',
        'model.posts.*.comments.*.comment' => '',
        'model.posts.*.comments.*.author.name' => '',
        'model.posts.*.otherComments.*.comment' => '',
        'model.posts.*.otherComments.*.author.name' => '',
    ];

    protected function rules()
    {
        return $this->_rules;
    }

    public function mount($rules = null)
    {
        if (isset($rules)) {
            $this->_rules = $rules;
        }
    }
}

class Author extends Model
{
    protected $connection = 'testbench';
    protected $guarded = [];

    public function posts()
    {
        return $this->hasMany(Post::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    public function otherComments()
    {
        return $this->hasMany(Comment::class);
    }
}

class Post extends Model
{
    protected $connection = 'testbench';
    protected $guarded = [];

    public function author()
    {
        return $this->belongsTo(Author::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    public function otherComments()
    {
        return $this->hasMany(OtherComment::class);
    }
}

class Comment extends Model
{
    protected $connection = 'testbench';
    protected $guarded = [];

    public function author()
    {
        return $this->belongsTo(Author::class);
    }

    public function post()
    {
        return $this->belongsTo(Post::class);
    }
}

class OtherComment extends Model
{
    protected $connection = 'testbench';
    protected $guarded = [];

    public function author()
    {
        return $this->belongsTo(Author::class);
    }

    public function post()
    {
        return $this->belongsTo(Post::class);
    }
}


class Foo extends Model
{
    use Sushi;

    protected $casts = ['baz' => 'array', 'bob' => 'array', 'lob' => 'array', 'zap' => 'array'];

    protected function getRows()
    {
        return [[
            'bar' => 'rab',
            'bar_baz' => 'zab_rab',
            'baz' => json_encode(['zab', 'azb']),
            'bob' => json_encode(['obb']),
            'lob' => json_encode(['law' => []]),
            'zap' => json_encode([]),
        ]];
    }
}

class CamelFoo extends Model
{
    use Sushi;

    protected function getRows()
    {
        return [[
            'bar' => 'baz',
        ]];
    }
}

class ComponentWithCamelCasedModelProperty extends Component
{
    public $camelFoo;

    protected $rules = [
        'camelFoo.bar' => 'required',
    ];

    public function save()
    {
        $this->validate();
    }

    public function render()
    {
        return \view('dump-errors');
    }
}

class ComponentForEloquentModelHydrationMiddleware extends Component
{
    public $foo;

    protected $rules = [
        'foo.bar' => 'required',
        'foo.bar_baz' => 'required',
        'foo.baz' => 'required|array|min:2',
        'foo.bob.*' => 'required|min:2',
        'foo.lob.law.*' => 'required|array',
        'foo.lob.law.*.blog' => 'required|min:5',
        'foo.zap.*.*.name' => 'required|min:3',
    ];

    public function save()
    {
        $this->validate();

        $this->foo->save();
    }

    public function performValidateOnly($field)
    {
        $this->validateOnly($field);
    }

    public function render()
    {
        return \view('dump-errors');
    }
}

class ComponentForEloquentModelCollectionHydrationMiddleware extends Component
{
    public $foos;

    protected $rules = [
        'foos' => 'required',
        'foos.*' => 'max:20',
        'foos.*.bar_baz' => 'required|min:10',
        'foos.*.bar' => 'required|min:10',
    ];

    public function performValidateOnly($field)
    {
        $this->validateOnly($field);
    }

    public function render()
    {
        return \view('dump-errors');
    }
}

class Items extends Model
{
    use Sushi;

    protected $rows = [
        ['title' => 'Lawn Mower', 'price' => '226.99', 'cart_id' => 1],
        ['title' => 'Leaf Blower', 'price' => '134.99', 'cart_id' => 1],
        ['title' => 'Rake', 'price' => '9.99', 'cart_id' => 1],
        ['title' => 'Lawn Mower', 'price' => '226.99', 'cart_id' => 2],
        ['title' => 'Leaf Blower', 'price' => '134.99', 'cart_id' => 2],
        ['title' => 'Lawn Mower', 'price' => '226.99', 'cart_id' => 3],
        ['title' => 'Leaf Blower', 'price' => '134.99', 'cart_id' => 3],
        ['title' => 'Rake', 'price' => '9.99', 'cart_id' => 3],
    ];

    protected $schema = [
        'price' => 'float',
    ];
}

class Cart extends Model
{
    use Sushi;

    protected $rows = [
        ['id' => 1, 'name' => 'Bob'],
        ['id' => 2, 'name' => 'John'],
        ['id' => 3, 'name' => 'Mark'],
    ];

    public function items()
    {
        return $this->hasMany(Items::class, 'cart_id', 'id');
    }
}

class ComponentForEloquentModelNestedHydrationMiddleware extends Component
{
    public $cart;

    protected $rules = [
        'cart.items.*.title' => 'required',
    ];

    public function save()
    {
        $this->validate();

        $this->cart->items->each->save();
    }

    public function render()
    {
        return \view('dump-errors');
    }
}


class ModelForAttributeCasting extends \Illuminate\Database\Eloquent\Model
{
    use Sushi;

    protected $guarded = [];

    protected $casts = [
        'normal_date' => 'date',
        'formatted_date' => 'date:d-m-Y',
        'date_with_time' => 'datetime',
        'timestamped_date' => 'timestamp',
        'integer_number' => 'integer',
        'real_number' => 'real',
        'float_number' => 'float',
        'double_precision_number' => 'double',
        'decimal_with_one_digit' => 'decimal:1',
        'decimal_with_two_digits' => 'decimal:2',
        'string_name' => 'string',
        'boolean_value' => 'boolean',
        'array_list' => 'array',
        'json_list' => 'json',
        'collected_list' => 'collection',
        'object_value' => 'object',
        'custom_caster' => QuizAnswerCaster::class,
        'enum' => TestingEnum::class,
    ];

    public function getRows()
    {
        return [
            [
                'normal_date' => new \DateTime('2000-08-12'),
                'formatted_date' => new \DateTime('2020-03-03'),
                'date_with_time' => new \DateTime('2015-10-21'),
                'timestamped_date' => new \DateTime('2002-08-30'),
                'integer_number' => 1,
                'real_number' => 2,
                'float_number' => 3,
                'double_precision_number' => 4,
                'decimal_with_one_digit' => 5,
                'decimal_with_two_digits' => 6,
                'string_name' => 'Gladys',
                'boolean_value' => false,
                'array_list' => json_encode([]),
                'json_list' => json_encode([1, 2, 3]),
                'collected_list' => json_encode([true, false]),
                'object_value' => json_encode(['name' => 'Marian', 'email' => 'marian@likes.pizza']),
                'custom_caster' => 'dumb answer',
                'enum' => null,
            ]
        ];
    }
}

class QuizAnswer
{
    protected $answer;

    public static function make(string $answer): self
    {
        $new = new static();
        $new->answer = $answer;

        return $new;
    }

    public function getAnswer(): string
    {
        return $this->answer;
    }

    public function matches($givenAnswer): bool
    {
        return $this->answer === $givenAnswer;
    }

    public function __toString()
    {
        return $this->getAnswer();
    }
}

class QuizAnswerCaster implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes)
    {
        return QuizAnswer::make((string) $value);
    }

    public function set($model, string $key, $value, array $attributes)
    {
        if ($value instanceof QuizAnswer) {
            $value = $value->getAnswer();
        }

        return $value;
    }
}

enum TestingEnum: string
{
    case FOO = 'bar';
}

class ComponentForModelAttributeCasting extends TestComponent
{
    public $model;

    public function rules(): array
    {
        return [
            'model.normal_date' => ['required', 'date'],
            'model.formatted_date' => ['required', 'date'],
            'model.date_with_time' => ['required', 'date'],
            'model.timestamped_date' => ['required', 'integer'],
            'model.integer_number' => ['required', 'integer'],
            'model.real_number' => ['required', 'numeric'],
            'model.float_number' => ['required', 'numeric'],
            'model.double_precision_number' => ['required', 'numeric'],
            'model.decimal_with_one_digit' => ['required', 'numeric'],
            'model.decimal_with_two_digits' => ['required', 'numeric'],
            'model.string_name' => ['required', 'string'],
            'model.boolean_value' => ['required', 'boolean'],
            'model.array_list' => ['required', 'array'],
            'model.array_list.*' => ['required', 'string'],
            'model.json_list' => ['required', 'array'],
            'model.json_list.*' => ['required', 'numeric'],
            'model.collected_list' => ['required'],
            'model.collected_list.*' => ['required', 'boolean'],
            'model.object_value' => ['required'],
            'model.object_value.name' => ['required'],
            'model.object_value.email' => ['required', 'email'],
            'model.custom_caster' => ['required'],
            'model.enum' => ['nullable', Rule::enum(TestingEnum::class)]
        ];
    }

    public function mount() {
        $this->model = ModelForAttributeCasting::first();
    }

    public function validateAttribute(string $attribute)
    {
        $this->validateOnly($attribute);
    }
}


<?php

namespace Livewire\Features\SupportErrorResponses;

use Livewire\Component as BaseComponent;
use Livewire\Livewire;

class BrowserTest extends \Tests\BrowserTestCase
{
    public function test_it_shows_page_expired_dialog_when_session_has_expired()
    {
        Livewire::visit(Component::class)
            ->waitForLivewire()->click('@regenerateSession')
            ->click('@refresh')
            // Wait for Livewire to respond, but dusk helper won't
            // work as dialog box is stopping further execution
            ->waitForDialog()
            ->assertDialogOpened("This page has expired.\nWould you like to refresh the page?")
            // Dismiss dialog so next tests run
            ->dismissDialog()
        ;
    }

    public function test_it_shows_custom_hook_dialog_using_on_error_response_hook_when_session_has_expired()
    {
        Livewire::withQueryParams(['useCustomErrorResponseHook' => true])
            ->visit(Component::class)
            ->waitForLivewire()->click('@regenerateSession')
            ->click('@refresh')
            // Wait for Livewire to respond, but dusk helper won't
            // work as dialog box is stopping further execution
            ->waitForDialog()
            ->assertDialogOpened('Page Expired - Error Response')
            // Dismiss dialog so next tests run
            ->dismissDialog()
        ;
    }
}

class Component extends BaseComponent
{
    public $useCustomPageExpiredHook = false;
    public $useCustomErrorResponseHook = false;

    protected $queryString = [
        'useCustomPageExpiredHook' => ['except' => false],
        'useCustomErrorResponseHook' => ['except' => false],
    ];

    public function regenerateSession()
    {
        request()->session()->regenerate();
    }

    public function render()
    {
        return <<< 'HTML'
<div>
    <button type="button" wire:click="regenerateSession" dusk="regenerateSession">Regenerate Session</button>
    <button type="button" wire:click="$refresh" dusk="refresh">Refresh</button>

    @if($useCustomErrorResponseHook)
    <script>
        document.addEventListener('livewire:init', () => {
            Livewire.hook('request', ({ fail, preventDefault }) => {
                fail(({ status }) => {
                    if (status === 419) {
                        confirm('Page Expired - Error Response')

                        preventDefault()
                    }
                })
            })
        })
    </script>
    @endif
</div>
HTML;
    }
}


<?php

namespace Livewire\Features\SupportLocales;

use Illuminate\Support\Facades\App;
use Livewire\Livewire;
use Tests\TestComponent;

class UnitTest extends \Tests\TestCase
{
    public function test_a_livewire_component_can_persist_its_locale()
    {
        // Set locale
        App::setLocale('en');
        $this->assertEquals(App::getLocale(), 'en');

        // Mount component and new ensure locale is set
        $component = Livewire::test(ComponentForLocalePersistanceHydrationMiddleware::class);
        $this->assertEquals(App::getLocale(), 'es');

        // Reset locale to ensure it isn't persisted in the test session
        App::setLocale('en');
        $this->assertEquals(App::getLocale(), 'en');

        // Verify locale is persisted from component mount
        $component->call('$refresh');
        $this->assertEquals(App::getLocale(), 'es');
    }
}

class ComponentForLocalePersistanceHydrationMiddleware extends TestComponent
{
    public function mount()
    {
        App::setLocale('es');
    }
}


<?php

namespace Livewire\Features\SupportFileUploads;

use Illuminate\Support\Facades\Storage;
use League\Flysystem\WhitespacePathNormalizer;

class FileUploadConfiguration
{
    public static function storage()
    {
        $disk = static::disk();

        if (app()->runningUnitTests()) {
            // We want to "fake" the first time in a test run, but not again because
            // Storage::fake() wipes the storage directory every time its called.
            rescue(
                // If the storage disk is not found (meaning it's the first time),
                // this will throw an error and trip the second callback.
                fn() => Storage::disk($disk),
                fn() => Storage::fake($disk),
                // swallows the error that is thrown on the first try
                report: false
            );
        }

        return Storage::disk($disk);
    }

    public static function disk()
    {
        if (app()->runningUnitTests()) {
            return 'tmp-for-tests';
        }

        return config('livewire.temporary_file_upload.disk') ?: config('filesystems.default');
    }

    public static function diskConfig()
    {
        return config('filesystems.disks.'.static::disk());
    }

    public static function isUsingS3()
    {
        $diskBeforeTestFake = config('livewire.temporary_file_upload.disk') ?: config('filesystems.default');

        return config('filesystems.disks.'.strtolower($diskBeforeTestFake).'.driver') === 's3';
    }

    public static function isUsingGCS()
    {
        $diskBeforeTestFake = config('livewire.temporary_file_upload.disk') ?: config('filesystems.default');

        return config('filesystems.disks.'.strtolower($diskBeforeTestFake).'.driver') === 'gcs';
    }

    public static function normalizeRelativePath($path)
    {
        return (new WhitespacePathNormalizer)->normalizePath($path);
    }

    public static function directory()
    {
        return static::normalizeRelativePath(config('livewire.temporary_file_upload.directory') ?: 'livewire-tmp');
    }

    protected static function s3Root()
    {
        if (! static::isUsingS3()) return '';

        $diskConfig = static::diskConfig();

        if (! is_array($diskConfig)) return '';

        $root = $diskConfig['root'] ?? null;

        return $root !== null ? static::normalizeRelativePath($root) : '';
    }

    public static function path($path = '', $withS3Root = true)
    {
        $prefix = $withS3Root ? static::s3Root() : '';
        $directory = static::directory();
        $path = static::normalizeRelativePath($path);

        return $prefix.($prefix ? '/' : '').$directory.($path ? '/' : '').$path;
    }

    public static function mimeType($filename)
    {
        $mimeType = static::storage()->mimeType(static::path($filename));

        return $mimeType === 'image/svg' ? 'image/svg+xml' : $mimeType;
    }

    public static function lastModified($filename)
    {
        return static::storage()->lastModified($filename);
    }

    public static function middleware()
    {
        return config('livewire.temporary_file_upload.middleware') ?: 'throttle:60,1';
    }

    public static function shouldCleanupOldUploads()
    {
        return config('livewire.temporary_file_upload.cleanup', true);
    }

    public static function rules()
    {
        $rules = config('livewire.temporary_file_upload.rules');

        if (is_null($rules)) return ['required', 'file', 'max:12288'];

        if (is_array($rules)) return $rules;

        return explode('|', $rules);
    }

    public static function maxUploadTime()
    {
        return config('livewire.temporary_file_upload.max_upload_time') ?: 5;
    }
}


<?php

namespace Livewire\Features\SupportNavigate;

use Laravel\Dusk\Browser;
use Livewire\Attributes\On;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\Drawer\Utils;
use Livewire\Livewire;

class BrowserTest extends \Tests\BrowserTestCase
{
    public static function tweakApplicationHook()
    {
        return function () {
            View::addNamespace('test-views', __DIR__ . '/test-views');

            Livewire::component('query-page', QueryPage::class);
            Livewire::component('first-page', FirstPage::class);
            Livewire::component('first-page-child', FirstPageChild::class);
            Livewire::component('first-page-with-link-outside', FirstPageWithLinkOutside::class);
            Livewire::component('second-page', SecondPage::class);
            Livewire::component('third-page', ThirdPage::class);
            Livewire::component('first-asset-page', FirstAssetPage::class);
            Livewire::component('second-asset-page', SecondAssetPage::class);
            Livewire::component('third-asset-page', ThirdAssetPage::class);
            Livewire::component('first-tracked-asset-page', FirstTrackedAssetPage::class);
            Livewire::component('second-tracked-asset-page', SecondTrackedAssetPage::class);
            Livewire::component('second-remote-asset', SecondRemoteAsset::class);
            Livewire::component('first-scroll-page', FirstScrollPage::class);
            Livewire::component('second-scroll-page', SecondScrollPage::class);
            Livewire::component('parent-component', ParentComponent::class);
            Livewire::component('child-component', ChildComponent::class);
            Livewire::component('script-component', ScriptComponent::class);

            Livewire::component('nav-bar-component', NavBarComponent::class);

            Route::get('/navbar/{page}', NavBarComponent::class)->middleware('web');

            Route::get('/query-page', QueryPage::class)->middleware('web');
            Route::get('/first', FirstPage::class)->middleware('web');
            Route::get('/first-hide-progress', function () {
                config(['livewire.navigate.show_progress_bar' => false]);

                return (new FirstPage)();
            })->middleware('web');
            Route::get('/first-outside', FirstPageWithLinkOutside::class)->middleware('web');
            Route::get('/redirect-to-second', fn () => redirect()->to('/second'));
            Route::get('/second', SecondPage::class)->middleware('web');
            Route::get('/third', ThirdPage::class)->middleware('web');
            Route::get('/fourth', FourthPage::class)->middleware('web');
            Route::get('/first-asset', FirstAssetPage::class)->middleware('web');
            Route::get('/second-asset', SecondAssetPage::class)->middleware('web');
            Route::get('/third-asset', ThirdAssetPage::class)->middleware('web');
            Route::get('/first-scroll', FirstScrollPage::class)->middleware('web');
            Route::get('/second-scroll', SecondScrollPage::class)->middleware('web');
            Route::get('/second-remote-asset', SecondRemoteAsset::class)->middleware('web');

            Route::get('/first-tracked-asset', FirstTrackedAssetPage::class)->middleware('web');
            Route::get('/second-tracked-asset', SecondTrackedAssetPage::class)->middleware('web');

            Route::get('/test-navigate-asset.js', function () {
                return Utils::pretendResponseIsFile(__DIR__ . '/test-views/test-navigate-asset.js');
            });

            Route::get('/parent', ParentComponent::class)->middleware('web');
            Route::get('/page-with-link-to-page-without-livewire', PageWithLinkAway::class);
            Route::get('/page-without-livewire-component', fn () => Blade::render(<<<'HTML'

                        <meta name="empty-layout" content>

This is a page without a livewire component

            HTML));

            Route::get('/page-with-alpine-for-loop', PageWithAlpineForLoop::class);
            Route::get('/script-component', ScriptComponent::class);
        };
    }

    public function test_back_button_works_with_teleports()
    {
        $this->registerComponentTestRoutes([
            '/second' => new class extends Component {
                public function render(){ return <<<'HTML'

                        On second page

                HTML; }
            },
        ]);

        Livewire::visit(new class extends Component {
            public function render(){
                return <<<'HTML'

                        Livewire component...

                        <template x-teleport="body">

                                <button x-on:click="outerScopeCount++" dusk="button">inc</button>

                        </template>

                        Go to second page

                HTML;
            }
        })
        ->assertSeeIn('@target', '0')
        ->click('@button')
        ->assertSeeIn('@target', '1')
        ->click('@link')
        ->waitForText('On second page')
        ->back()
        ->assertDontSee('On second page')
        ->assertSeeIn('@target', '0')
        ->click('@button')
        ->assertSeeIn('@target', '1')
        ->forward()
        ->back()
        ->assertSeeIn('@target', '0')
        ->click('@button')
        ->assertSeeIn('@target', '1')
        ;
    }

    public function test_back_button_works_with_teleports_inside_persist()
    {
        $this->registerComponentTestRoutes([
            '/second' => new class extends Component {
                public function render(){ return <<<'HTML'

                            On second page

                        @persist('header')

                                <template x-teleport="body">

                                        <button x-on:click="outerScopeCount++" dusk="button">inc</button>

                                </template>

                        @endpersist

                HTML; }
            },
        ]);

        Livewire::visit(new class extends Component {
            public function render(){
                return <<<'HTML'

                            On first page

                        @persist('header')

                                <template x-teleport="body">

                                        <button x-on:click="outerScopeCount++" dusk="button">inc</button>

                                </template>

                        @endpersist

                        Go to second page

                HTML;
            }
        })
        ->assertSeeIn('@target', '0')
        ->click('@button')
        ->assertSeeIn('@target', '1')
        ->click('@link')
        ->waitForText('On second page')
        ->assertSeeIn('@target', '1')
        ->click('@button')
        ->assertSeeIn('@target', '2')
        ->back()
        ->assertSeeIn('@target', '2')
        ->click('@button')
        ->assertSeeIn('@target', '3')
        ->forward()
        ->assertSeeIn('@target', '3')
        ->click('@button')
        ->assertSeeIn('@target', '4')
        ;
    }

    public function test_can_configure_progress_bar()
    {
        $this->browse(function ($browser) {
            $browser
                ->visit('/first')
                ->tap(fn ($b) => $b->script('window._lw_dusk_test = true'))
                ->assertScript('return window._lw_dusk_test')
                ->assertSee('On first')
                ->click('@link.to.third')
                ->waitFor('#nprogress')
                ->waitForText('Done loading...');
        });

        $this->browse(function ($browser) {
            $browser
                ->visit('/first-hide-progress')
                ->tap(fn ($b) => $b->script('window._lw_dusk_test = true'))
                ->assertScript('return window._lw_dusk_test')
                ->assertSee('On first')
                ->click('@link.to.third')
                ->pause(500)
                ->assertMissing('#nprogress')
                ->waitForText('Done loading...');
        });
    }


public function test_navigate_scrolls_to_top_and_back_preserves_scroll()
    {
        $this->browse(function ($browser) {
            $browser
                ->visit('/first-scroll')
                ->assertVisible('@first-target')
                ->assertNotInViewPort('@first-target')
                ->scrollTo('@first-target')
                ->assertInViewPort('@first-target')

                ->click('@link.to.second')
                ->waitForText('On second')
                ->assertNotInViewPort('@second-target')
                ->scrollTo('@second-target')

                ->back()
                ->waitForText('On first')
                ->assertInViewPort('@first-target')

                ->forward()
                ->waitForText('On second')
                ->assertInViewPort('@second-target')
            ;
        });
    }

    public function test_navigate_back_works_from_page_without_a_livewire_component_that_has_a_script_with_data_navigate_track()
    {
        // When using `@vite` on the page without a Livewire component,
        // it injects a script tag with `data-navigate-track`,
        // which causes Livewire to be unloaded and the back button no longer work.
        $this->browse(function ($browser) {
            $browser
                ->visit('/page-with-link-to-page-without-livewire')
                ->assertSee('Link to page without Livewire component')
                ->assertDontSee('This is a page without a livewire component')
                ->click('@link.away')
                ->waitFor('@non-livewire-page')
                ->assertSee('This is a page without a livewire component')
                ->assertDontSee('Link to page without Livewire component')
                ->back()
                ->waitFor('@page-with-link-away')
                ->assertSee('Link to page without Livewire component')
                ->assertDontSee('This is a page without a livewire component')
            ;
        });
    }

    public function test_navigate_is_only_triggered_on_left_click()
    {
        $this->browse(function ($browser) {
            $browser
                ->visit('/first')
                ->tap(fn ($b) => $b->script('window._lw_dusk_test = true'))
                ->assertScript('return window._lw_dusk_test')
                ->assertSee('On first')
                ->rightClick('@link.to.second')
                ->pause(500) // Let navigate run if it was going to (it should not)
                ->assertSee('On first')
                ->click('@link.to.second')
                ->waitFor('@link.to.first')
                ->assertSee('On second')
                ;
        });
    }

    public function test_livewire_navigated_event_is_fired_on_first_page_load()
    {
        $this->browse(function ($browser) {
            $browser
                ->visit('/second')
                ->assertSee('On second')
                ->assertScript('window.foo_navigated', 'bar');
        });
    }

    public function test_livewire_before_navigate_event_is_fired_when_click()
    {
        $this->browse(function($browser) {
            $browser
                ->visit('/fourth')
                ->assertSee('On fourth')
                ->assertScript('window.foo', 'bar')
                ->assertSee('On fourth')
                ->click('@link.to.first') // first attempt bar -> baz
                ->assertScript('window.foo', 'baz')
                ->assertSee('On fourth')
                ->click('@link.to.first') // second attempt baz -> bat
                ->assertScript('window.foo', 'bat')
                ->assertSee('On fourth')
                ->click('@link.to.first') // finally navigate
                ->assertSee('On first')
            ;
        });
    }

    public function test_livewire_navigated_event_is_fired_after_redirect_without_reloading()
    {
        $this->browse(function ($browser) {
            $browser
                ->visit('/first')
                ->tap(fn ($b) => $b->script('window._lw_dusk_test = true'))
                ->assertScript('return window._lw_dusk_test')
                ->assertSee('On first')
                ->click('@link.to.second')
                ->waitFor('@link.to.first')
                ->assertSee('On second')
                ->assertScript('window.foo_navigated', 'bar');
        });
    }

    public function test_navigate_is_not_triggered_on_cmd_click()
    {
        $key = PHP_OS_FAMILY === 'Darwin' ? \Facebook\WebDriver\WebDriverKeys::COMMAND : \Facebook\WebDriver\WebDriverKeys::CONTROL;

        $this->browse(function (Browser $browser) use ($key) {
            $currentWindowHandles = count($browser->driver->getWindowHandles());

            $browser
                ->visit('/first')
                ->tap(fn ($b) => $b->script('window._lw_dusk_test = true'))
                ->assertScript('return window._lw_dusk_test')
                ->assertSee('On first')
                ->tap(function ($browser) use ($key) {
                    $browser->driver->getKeyboard()->pressKey($key);
                })
                ->click('@link.to.second')
                ->tap(function ($browser) use ($key) {
                    $browser->driver->getKeyboard()->releaseKey($key);
                })
                ->pause(500) // Let navigate run if it was going to (it should not)
                ->assertSee('On first')
                ->assertScript('return window._lw_dusk_test')
            ;

            $this->assertCount($currentWindowHandles + 1, $browser->driver->getWindowHandles());
        });
    }

    public function test_events_from_child_components_still_function_after_navigation()
    {
        $this->browse(function (Browser $browser) {
            $browser
                ->visit('/parent')
                ->assertSeeNothingIn('@text-child')
                ->assertSeeNothingIn('@text-parent')
                ->waitForLivewire()->type('@text-input', 'test')
                ->waitForTextIn('@text-child', 'test')
                ->waitForTextIn('@text-parent', 'test')

                ->waitForNavigate()->click('@home-link')
                ->assertSeeNothingIn('@text-child')
                ->assertSeeNothingIn('@text-parent')
                ->waitForLivewire()->type('@text-input', 'testing')
                ->waitForTextIn('@text-child', 'testing')
                ->waitForTextIn('@text-parent', 'testing')

                ->back()
                ->waitForTextIn('@text-child', 'test')
                ->waitForTextIn('@text-parent', 'test')
                ->waitForLivewire()->type('@text-input', 'testing')
                ->waitForTextIn('@text-child', 'testing')
                ->waitForTextIn('@text-parent', 'testing');
        });
    }

    public function test_alpine_for_loop_still_functions_after_navigation()
    {
        $this->browse(function (Browser $browser) {
            $browser
                ->visit('/page-with-alpine-for-loop')
                ->assertSeeIn('@text', 'a,b,c')
                ->assertScript('document.getElementById(\'alpine-for-loop\').querySelectorAll(\'p\').length', 3)
                ->assertConsoleLogMissingWarning('value is not defined')

                ->waitForNavigate()->click('@link.to.second')
                ->assertSee('On second')

                ->back()
                ->assertSeeIn('@text', 'a,b,c')
                ->assertScript('document.getElementById(\'alpine-for-loop\').querySelectorAll(\'p\').length', 3)
                ->assertConsoleLogMissingWarning('value is not defined')
            ;
        });
    }

    public function test_injected_assets_such_as_nprogress_styles_are_retained_when_the_page_changes()
    {
        $this->browse(function ($browser) {
            $browser
                ->visit('/first')
                ->tap(fn ($b) => $b->script('window._lw_dusk_test = true'))
                ->assertScript('return window._lw_dusk_test')
                ->assertSee('On first')
                // There should only be two style blocks, livewire styles and nprogress
                ->assertScript('return document.styleSheets.length', 2)
                ->click('@link.to.second')
                ->waitFor('@link.to.first')
                ->assertSee('On second')
                ->assertScript('return window._lw_dusk_test')
                // There should only be two style blocks, livewire styles and nprogress
                ->assertScript('return document.styleSheets.length', 2)
                ->click('@link.to.first')
                ->waitFor('@link.to.second')
                ->assertScript('return window._lw_dusk_test')
                ->assertSee('On first')
                // There should only be two style blocks, livewire styles and nprogress
                ->assertScript('return document.styleSheets.length', 2);
        });
    }

    public function test_remote_assets_loaded_with_the_directive_fully_load_before_component_scripts_and_initialization()
    {
        $this->browse(function ($browser) {
            $browser
                ->visit('/first')
                ->assertSee('On first')
                // There should only be two style blocks, livewire styles and nprogress
                ->click('@link.to.asset')
                ->waitFor('@target')
                ->waitForTextIn('@target', 'bar')
                ;
        });
    }


<html>
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <script src="/test-navigate-asset.js?v=123" data-navigate-track></script>
</head>
<body>
    {{ $slot }}

    @stack('scripts')
</body>
</html>


<html>
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <script src="/test-navigate-asset.js?v=123"></script>
</head>
<body>
    {{ $slot }}

    @stack('scripts')
</body>
</html>


<html>
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <script src="/test-navigate-asset.js?v=456" data-navigate-track></script>
</head>
<body>
    {{ $slot }}

    @stack('scripts')
</body>
</html>


<html>
<head>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <script src="/test-navigate-asset.js?v=456"></script>
</head>
<body>
    {{ $slot }}

    @stack('scripts')
</body>
</html>


<?php

namespace Livewire\Features\SupportDataBinding;

use Tests\BrowserTestCase;
use Livewire\Livewire;
use Livewire\Component;
use Livewire\Attributes\Computed;

class BrowserTest extends BrowserTestCase
{
    function test_can_use_wire_dirty()
    {
        Livewire::visit(new class extends Component {
            public $prop = false;

            public function render()
            {
                return <<<'BLADE'

                        <input dusk="checkbox" type="checkbox" wire:model="prop" value="true"  />

Unsaved changes...

The data is in-sync...

                BLADE;
            }
        })
            ->assertSee('The data is in-sync...')
            ->check('@checkbox')
            ->assertDontSee('The data is in-sync')
            ->assertSee('Unsaved changes...')
            ->uncheck('@checkbox')
            ->assertSee('The data is in-sync...')
            ->assertDontSee('Unsaved changes...')
        ;
    }

    function test_can_update_bound_value_from_lifecyle_hook()
    {
        Livewire::visit(new class extends Component {
            public $foo = null;

            public $bar = null;

            public function updatedFoo(): void
            {
                $this->bar = null;
            }

            public function render()
            {
                return <<<'BLADE'

                        <select wire:model.live="foo" dusk="fooSelect">
                            <option value=""></option>
                            <option value="one">One</option>
                            <option value="two">Two</option>
                            <option value="three">Three</option>
                        </select>

                        <select wire:model="bar" dusk="barSelect">
                            <option value=""></option>
                            <option value="one">One</option>
                            <option value="two">Two</option>
                            <option value="three">Three</option>
                        </select>

                BLADE;
            }
        })
            ->select('@barSelect', 'one')
            ->waitForLivewire()->select('@fooSelect', 'one')
            ->assertSelected('@barSelect', '')
        ;
    }

    public function updates_dependent_select_options_correctly_when_wire_key_is_applied()
    {
        Livewire::visit(new class extends Component {
            public $parent = 'foo';

            public $child = 'bar';

            protected $options = [
                'foo' => [
                    'bar',
                ],
                'baz' => [
                    'qux',
                ],
            ];

            #[Computed]
            public function parentOptions(): array
            {
                return array_keys($this->options);
            }

            #[Computed]
            public function childOptions(): array
            {
                return $this->options[$this->parent];
            }

            public function render(): string
            {
                return <<<'blade'

                        <select wire:model.live="parent" dusk="parent">
                            @foreach($this->parentOptions as $value)
                                <option value="{{ $value }}">{{ $value }}</option>
                            @endforeach
                        </select>

                        <select wire:model="child" dusk="child" wire:key="{{ $parent }}">
                            <option value>Select</option>
                            @foreach($this->childOptions as $value)
                                <option value="{{ $value }}">{{ $value }}</option>
                            @endforeach
                        </select>

                blade;
            }
        })
            ->waitForLivewire()->select('@parent', 'baz')
            ->assertSelected('@child', '')
            ->waitForLivewire()->select('@parent', 'foo')
            ->assertSelected('@child', 'bar');
    }
}


<?php

namespace Livewire\Tests;

use Livewire\Component;
use Livewire\Livewire;

class AlpineUiBrowserTest extends \Tests\BrowserTestCase
{
    public function test_component_with_listbox_and_wire_model_live_should_not_cause_infinite_loop()
    {
        Livewire::visit(new class extends Component {
            public ?array $foo = null;

            function render() {
                return <<<'HTML'

                    <button wire:click="$refresh">refresh</button>

updates:

                            Backend framework

                            <button x-listbox:button dusk="openListbox">

                            </button>

                                <template x-for="framework in frameworks" :key="framework.id">

                                </template>

                HTML;
            }
        })
            ->waitForLivewireToLoad()
            ->click('@openListbox')
            ->assertSeeIn('@updatesCount', '1')
            ->pressAndWaitFor('@listboxOption', 250)
            ->assertSeeIn('@updatesCount', '2')
        ;
    }

    public function test_component_with_combobox_and_wire_model_live_should_not_cause_infinite_loop()
    {
        Livewire::visit(new class extends Component {
            public ?array $value = null;

            function render() {
                return <<<'HTML'

                    <div
                        x-data="{
                            query: '',
                            selected: null,
                            frameworks: [{
                                id: 1,
                                name: 'Laravel',
                                disabled: false,
                            }, ],
                            get filteredFrameworks() {
                                return this.query === '' ?
                                    this.frameworks :
                                    this.frameworks.filter((framework) => {
                                        return framework.name.toLowerCase().includes(this.query.toLowerCase())
                                    })
                            },
                            updates: 0,
                        }" x-modelable="selected" wire:model.live="value" x-effect="console.log(selected); updates++">

updates:

                                    <input
                                        x-combobox:input
                                        :display-value="framework => framework?.name"
                                        @change="query = $event.target.value;"
                                        placeholder="Search..." />
                                    <button x-combobox:button dusk="openCombobox">
                                        open combobox
                                    </button>

                                        <template
                                            x-for="framework in filteredFrameworks"
                                            :key="framework.id"
                                            hidden>

                                        </template>

No frameworks match your query.

                HTML;
            }
        })
            ->waitForLivewireToLoad()
            ->click('@openCombobox')
            ->assertSeeIn('@updatesCount', '1')
            ->pressAndWaitFor('@comboboxOption', 250)
            ->assertSeeIn('@updatesCount', '2')
        ;
    }
}


<?php

namespace Livewire\Tests;

use Livewire\Component;
use Livewire\Livewire;

class UpdatingTableRowsTest extends \Tests\BrowserTestCase
{
    public function test_component_renders_table_rows_and_updates_properly()
    {
        Livewire::visit([new class extends Component {
            public function render() {
                return <<<'HTML'

                            <livewire:child />

                HTML;
            }
        },
        'child' => new class extends Component {
            public int $counter = 0;

            public function increment()
            {
                $this->counter++;
            }

            public function render()
            {
                return <<<'HTML'

                            <button type="button" wire:click="increment" dusk="increment">+</button>

                            <input wire:model="counter" dusk="counter">

                HTML;
            }
        }])
            ->assertPresent('@table-row')
            ->assertPresent('@counter')
            ->assertInputValue('@counter', '0')
            ->click('@increment')
            ->waitForLivewire()
            ->assertPresent('@table-row')
            ->assertPresent('@counter')
            ->assertInputValue('@counter', '1')
        ;
    }
}


<?php

namespace Livewire\Exceptions;

use Symfony\Component\HttpKernel\Exception\HttpException;

class LivewirePageExpiredBecauseNewDeploymentHasSignificantEnoughChanges extends HttpException
{
    public function __construct()
    {
        parent::__construct(
            419,
            'New deployment contains changes to Livewire that have invalidated currently open browser pages.'
        );
    }
}


## About Laravel

Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:

- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).

Laravel is accessible, powerful, and provides tools required for large, robust applications.

## Learning Laravel

Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.

You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.

If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.

## Laravel Sponsors

We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).

### Premium Partners

- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[WebReinvent](https://webreinvent.com/)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
- **[Jump24](https://jump24.co.uk)**
- **[Redberry](https://redberry.international/laravel/)**
- **[Active Logic](https://activelogic.com)**
- **[byte5](https://byte5.de)**
- **[OP.GG](https://op.gg)**

## Contributing

Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).

## Code of Conduct

In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).

## Security Vulnerabilities

If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.

## License

The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).


#!/usr/bin/env php
<?php

use Symfony\Component\Console\Input\ArgvInput;

define('LARAVEL_START', microtime(true));

// Register the Composer autoloader...
require __DIR__.'/vendor/autoload.php';

// Bootstrap Laravel and handle the command...
$status = (require_once __DIR__.'/bootstrap/app.php')
    ->handleCommand(new ArgvInput);

exit($status);


<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        //
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();


<?php

return [
    App\Providers\AppServiceProvider::class,
];


<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}


<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Application Name
    |--------------------------------------------------------------------------
    |
    | This value is the name of your application, which will be used when the
    | framework needs to place the application's name in a notification or
    | other UI elements where an application name needs to be displayed.
    |
    */

    'name' => env('APP_NAME', 'Laravel'),

    /*
    |--------------------------------------------------------------------------
    | Application Environment
    |--------------------------------------------------------------------------
    |
    | This value determines the "environment" your application is currently
    | running in. This may determine how you prefer to configure various
    | services the application utilizes. Set this in your ".env" file.
    |
    */

    'env' => env('APP_ENV', 'production'),

    /*
    |--------------------------------------------------------------------------
    | Application Debug Mode
    |--------------------------------------------------------------------------
    |
    | When your application is in debug mode, detailed error messages with
    | stack traces will be shown on every error that occurs within your
    | application. If disabled, a simple generic error page is shown.
    |
    */

    'debug' => (bool) env('APP_DEBUG', false),

    /*
    |--------------------------------------------------------------------------
    | Application URL
    |--------------------------------------------------------------------------
    |
    | This URL is used by the console to properly generate URLs when using
    | the Artisan command line tool. You should set this to the root of
    | the application so that it's available within Artisan commands.
    |
    */

    'url' => env('APP_URL', 'http://localhost'),

    /*
    |--------------------------------------------------------------------------
    | Application Timezone
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default timezone for your application, which
    | will be used by the PHP date and date-time functions. The timezone
    | is set to "UTC" by default as it is suitable for most use cases.
    |
    */

    'timezone' => env('APP_TIMEZONE', 'UTC'),

    /*
    |--------------------------------------------------------------------------
    | Application Locale Configuration
    |--------------------------------------------------------------------------
    |
    | The application locale determines the default locale that will be used
    | by Laravel's translation / localization methods. This option can be
    | set to any locale for which you plan to have translation strings.
    |
    */

    'locale' => env('APP_LOCALE', 'en'),

    'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),

    'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),

    /*
    |--------------------------------------------------------------------------
    | Encryption Key
    |--------------------------------------------------------------------------
    |
    | This key is utilized by Laravel's encryption services and should be set
    | to a random, 32 character string to ensure that all encrypted values
    | are secure. You should do this prior to deploying the application.
    |
    */

    'cipher' => 'AES-256-CBC',

    'key' => env('APP_KEY'),

    'previous_keys' => [
        ...array_filter(
            explode(',', env('APP_PREVIOUS_KEYS', ''))
        ),
    ],

    /*
    |--------------------------------------------------------------------------
    | Maintenance Mode Driver
    |--------------------------------------------------------------------------
    |
    | These configuration options determine the driver used to determine and
    | manage Laravel's "maintenance mode" status. The "cache" driver will
    | allow maintenance mode to be controlled across multiple machines.
    |
    | Supported drivers: "file", "cache"
    |
    */

    'maintenance' => [
        'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
        'store' => env('APP_MAINTENANCE_STORE', 'database'),
    ],

];


<?php

use Illuminate\Support\Str;

return [

    /*
    |--------------------------------------------------------------------------
    | Default Session Driver
    |--------------------------------------------------------------------------
    |
    | This option determines the default session driver that is utilized for
    | incoming requests. Laravel supports a variety of storage options to
    | persist session data. Database storage is a great default choice.
    |
    | Supported: "file", "cookie", "database", "apc",
    |            "memcached", "redis", "dynamodb", "array"
    |
    */

    'driver' => env('SESSION_DRIVER', 'database'),

    /*
    |--------------------------------------------------------------------------
    | Session Lifetime
    |--------------------------------------------------------------------------
    |
    | Here you may specify the number of minutes that you wish the session
    | to be allowed to remain idle before it expires. If you want them
    | to expire immediately when the browser is closed then you may
    | indicate that via the expire_on_close configuration option.
    |
    */

    'lifetime' => env('SESSION_LIFETIME', 120),

    'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),

    /*
    |--------------------------------------------------------------------------
    | Session Encryption
    |--------------------------------------------------------------------------
    |
    | This option allows you to easily specify that all of your session data
    | should be encrypted before it's stored. All encryption is performed
    | automatically by Laravel and you may use the session like normal.
    |
    */

    'encrypt' => env('SESSION_ENCRYPT', false),

    /*
    |--------------------------------------------------------------------------
    | Session File Location
    |--------------------------------------------------------------------------
    |
    | When utilizing the "file" session driver, the session files are placed
    | on disk. The default storage location is defined here; however, you
    | are free to provide another location where they should be stored.
    |
    */

    'files' => storage_path('framework/sessions'),

    /*
    |--------------------------------------------------------------------------
    | Session Database Connection
    |--------------------------------------------------------------------------
    |
    | When using the "database" or "redis" session drivers, you may specify a
    | connection that should be used to manage these sessions. This should
    | correspond to a connection in your database configuration options.
    |
    */

    'connection' => env('SESSION_CONNECTION'),

    /*
    |--------------------------------------------------------------------------
    | Session Database Table
    |--------------------------------------------------------------------------
    |
    | When using the "database" session driver, you may specify the table to
    | be used to store sessions. Of course, a sensible default is defined
    | for you; however, you're welcome to change this to another table.
    |
    */

    'table' => env('SESSION_TABLE', 'sessions'),

    /*
    |--------------------------------------------------------------------------
    | Session Cache Store
    |--------------------------------------------------------------------------
    |
    | When using one of the framework's cache driven session backends, you may
    | define the cache store which should be used to store the session data
    | between requests. This must match one of your defined cache stores.
    |
    | Affects: "apc", "dynamodb", "memcached", "redis"
    |
    */

    'store' => env('SESSION_STORE'),

    /*
    |--------------------------------------------------------------------------
    | Session Sweeping Lottery
    |--------------------------------------------------------------------------
    |
    | Some session drivers must manually sweep their storage location to get
    | rid of old sessions from storage. Here are the chances that it will
    | happen on a given request. By default, the odds are 2 out of 100.
    |
    */

    'lottery' => [2, 100],

    /*
    |--------------------------------------------------------------------------
    | Session Cookie Name
    |--------------------------------------------------------------------------
    |
    | Here you may change the name of the session cookie that is created by
    | the framework. Typically, you should not need to change this value
    | since doing so does not grant a meaningful security improvement.
    |
    */

    'cookie' => env(
        'SESSION_COOKIE',
        Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
    ),

    /*
    |--------------------------------------------------------------------------
    | Session Cookie Path
    |--------------------------------------------------------------------------
    |
    | The session cookie path determines the path for which the cookie will
    | be regarded as available. Typically, this will be the root path of
    | your application, but you're free to change this when necessary.
    |
    */

    'path' => env('SESSION_PATH', '/'),

    /*
    |--------------------------------------------------------------------------
    | Session Cookie Domain
    |--------------------------------------------------------------------------
    |
    | This value determines the domain and subdomains the session cookie is
    | available to. By default, the cookie will be available to the root
    | domain and all subdomains. Typically, this shouldn't be changed.
    |
    */

    'domain' => env('SESSION_DOMAIN'),

    /*
    |--------------------------------------------------------------------------
    | HTTPS Only Cookies
    |--------------------------------------------------------------------------
    |
    | By setting this option to true, session cookies will only be sent back
    | to the server if the browser has a HTTPS connection. This will keep
    | the cookie from being sent to you when it can't be done securely.
    |
    */

    'secure' => env('SESSION_SECURE_COOKIE'),

    /*
    |--------------------------------------------------------------------------
    | HTTP Access Only
    |--------------------------------------------------------------------------
    |
    | Setting this value to true will prevent JavaScript from accessing the
    | value of the cookie and the cookie will only be accessible through
    | the HTTP protocol. It's unlikely you should disable this option.
    |
    */

    'http_only' => env('SESSION_HTTP_ONLY', true),

    /*
    |--------------------------------------------------------------------------
    | Same-Site Cookies
    |--------------------------------------------------------------------------
    |
    | This option determines how your cookies behave when cross-site requests
    | take place, and can be used to mitigate CSRF attacks. By default, we
    | will set this value to "lax" to permit secure cross-site requests.
    |
    | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
    |
    | Supported: "lax", "strict", "none", null
    |
    */

    'same_site' => env('SESSION_SAME_SITE', 'lax'),

    /*
    |--------------------------------------------------------------------------
    | Partitioned Cookies
    |--------------------------------------------------------------------------
    |
    | Setting this value to true will tie the cookie to the top-level site for
    | a cross-site context. Partitioned cookies are accepted by the browser
    | when flagged "secure" and the Same-Site attribute is set to "none".
    |
    */

    'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),

];