Repository: roots/acorn (php)

Issue:
Illuminate\View\ViewServiceProvider is instantiated twice
### Version

4.2.2

### What did you expect to happen?

I'd expect `Illuminate\View\ViewServiceProvider` to only be instantiated/registered/booted once as a subclass of `Roots\Acorn\View\ViewServiceProvider`.

### What actually happens?

Both `lluminate\View\ViewServiceProvider` and `Roots\Acorn\View\ViewServiceProvider` are instantiated.

### Steps to reproduce

1. Set a breakpoint in `Illuminate\View\ViewServiceProvider::register()`
2. Boot Acorn
3. Watch the breakpoint trigger twice

### System info

Pop!_OS 22.04 LTS

### Log output

_No response_

### Please confirm this isn't a support request.

Yes
Cannot replace Roots\Acorn\View\ViewServiceProvider
### Version

4.2.2

### What did you expect to happen?

I'd expect the following code to replace `Roots\Acorn\View\ViewServiceProvider` with `App\Providers\ViewServiceProvider`.

```php
'providers' => ServiceProvider::defaultProviders()
    ->replace([
        Roots\Acorn\View\ViewServiceProvider::class => App\Providers\ViewServiceProvider::class,
    ])
    ->toArray(),
```

### What actually happens?

`Roots\Acorn\View\ViewServiceProvider` is merged back in by the constructor: https://github.com/roots/acorn/blob/97a50bb3c968d6a8862e52d49b6a9c861dc7624d/src/Roots/Acorn/DefaultProviders.php#L34.

### Steps to reproduce

1. Use the config from *What did you expect to happen?*
2. Place a breakpoint in `Roots\Acorn\View\ViewServiceProvider::register()`
3. Boot the application
4. Watch the breakpoint trigger twice

### System info

Pop!_OS 22.04 LTS

### Log output

_No response_

### Please confirm this isn't a support request.

Yes

Pull request:
🩹 Fix default provider discrepancies
## Change log

### Enhancements

- ✅ Add test for replacing default providers

### Bug fixes

- 🩹 Fix duplicate `ViewServiceProvider` registration (Fixes #374)
- 🩹 Fix replacing default providers (Fixes #375)

Patch:
diff --git a/src/Roots/Acorn/DefaultProviders.php b/src/Roots/Acorn/DefaultProviders.php
index 7317a99..99962fb 100644
--- a/src/Roots/Acorn/DefaultProviders.php
+++ b/src/Roots/Acorn/DefaultProviders.php
@@ -32,6 +32,7 @@ class DefaultProviders extends DefaultProvidersBase
      */
     protected array $disallowedProviders = [
         \Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
+        \Illuminate\View\ViewServiceProvider::class,
     ];
 
     /**
@@ -43,6 +44,12 @@ class DefaultProviders extends DefaultProvidersBase
     {
         parent::__construct($providers);
 
+        $this->providers = array_unique($this->providers);
+
+        if ($providers) {
+            return;
+        }
+
         $this->providers = Collection::make($this->providers)
             ->merge($this->acornProviders)
             ->filter(fn ($provider) => ! Str::contains($provider, $this->disallowedProviders))


Test patch:
diff --git a/tests/Application/DefaultProvidersTest.php b/tests/Application/DefaultProvidersTest.php
index cdb689e..df14a61 100644
--- a/tests/Application/DefaultProvidersTest.php
+++ b/tests/Application/DefaultProvidersTest.php
@@ -13,3 +13,14 @@ it('dedupes registered providers', function () {
 
     expect($providers->toArray())->toBe(array_unique($providers->toArray()));
 });
+
+it('replaces default providers', function () {
+    $providers = \Roots\Acorn\ServiceProvider::defaultProviders()->replace([
+        \Roots\Acorn\Providers\AcornServiceProvider::class => BootableServiceProvider::class,
+    ]);
+
+    expect($providers->toArray())
+        ->toContain(BootableServiceProvider::class)
+        ->not()
+        ->toContain(\Roots\Acorn\Providers\AcornServiceProvider::class);
+});