[
  {
    "path": ".gitattributes",
    "content": "/.gitattributes export-ignore\n/.github export-ignore\n/.gitignore export-ignore\n/tests export-ignore\n/phpunit.xml.dist export-ignore\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: Propaganistas\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Tests\n\non:\n  push:\n  pull_request:\n  schedule:\n    - cron: '0 0 * * 1'\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        php: [ 8.0, 8.1, 8.2, 8.3 ]\n        laravel: [ 9, 10 ]\n        stability: [ prefer-lowest, prefer-stable ]\n        exclude:\n          - php: 8.0\n            laravel: 10\n\n    name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} (${{ matrix.stability }})\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php }}\n          extensions: dom, curl, json, libxml, mbstring, zip\n          tools: composer:v2\n          coverage: none\n\n      # https://github.com/briannesbitt/Carbon/releases/tag/2.62.1\n      - name: Patch Carbon version\n        if: matrix.php == 8.2 || matrix.php == 8.3\n        run: |\n          composer require \"nesbot/carbon=^2.63\" --dev --no-interaction --no-update\n\n      - name: Install dependencies\n        run: |\n          composer require \"illuminate/support=^${{ matrix.laravel }}\" --no-interaction --no-update\n          composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress\n\n      - name: Execute tests\n        run: vendor/bin/phpunit --verbose\n"
  },
  {
    "path": ".gitignore",
    "content": "/vendor\n/.idea\ncomposer.phar\ncomposer.lock\n.phpunit.result.cache\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 \n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# 🚨 ABANDONED\n\nLooking for a new maintainer.\nIf no maintainer is found by December 2025, this repository will be deleted.\n\nUse [hashids](https://github.com/vinkla/hashids) instead.\n\n---\n\n---\n\n---\n\n# Laravel FakeID\n\n![Tests](https://github.com/Propaganistas/Laravel-FakeId/workflows/Tests/badge.svg?branch=master)\n[![Latest Stable Version](https://poser.pugx.org/propaganistas/laravel-fakeid/v/stable)](https://packagist.org/packages/propaganistas/laravel-fakeid)\n[![Total Downloads](https://poser.pugx.org/propaganistas/laravel-fakeid/downloads)](https://packagist.org/packages/propaganistas/laravel-fakeid)\n[![License](https://poser.pugx.org/propaganistas/laravel-fakeid/license)](https://packagist.org/packages/propaganistas/laravel-fakeid)\n\nEnables automatic Eloquent model ID obfuscation in routes using [Optimus](https://github.com/jenssegers/optimus).\n\n### Installation\n\n1. Run the Composer require command to install the package\n\n    ```bash\n    composer require propaganistas/laravel-fakeid\n    ```\n\n2. The package will automatically register itself.\n\n3. Run the following artisan command to auto-initialize the package's settings\n    \n    ```bash\n    php artisan fakeid:setup\n    ```\n\n### Usage\n\nSimply import the `RoutesWithFakeIds` trait into your model:\n\n```php\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Propaganistas\\LaravelFakeId\\RoutesWithFakeIds;\n\nclass MyModel extends Model\n{\n    use RoutesWithFakeIds;\n}\n```\n\nAll routes generated for this particular model will expose a **fake** ID instead of the raw primary key. Moreover incoming requests containing those fake IDs are automatically converted back to a real ID. The obfuscation layer is therefore transparent and doesn't require you to rethink everything. Just use Laravel as you normally would.\n\n### Example ###\nAssuming an `Article` model having a named `show` route.\n\n`routes/web.php`:\n\n```php\nRoute::get('articles/{article}', 'ArticleController@show')->name('articles.show');\n```\n\n`app/Article.php`\n\n```php\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Propaganistas\\LaravelFakeId\\RoutesWithFakeIds;\n\nclass Article extends Model\n{\n    use RoutesWithFakeIds;\n}\n```\n\nA route to this specific endpoint can now be generated using Laravel's `route()` helper, and it will automatically contain a **fake** ID:\n\n\n```php\n<a href=\"{{ route('articles.show', $article) }}\"> {{ $article->name }} </a>\n```\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"propaganistas/laravel-fakeid\",\n    \"description\": \"Automatic ID obfuscation for Eloquent models.\",\n    \"keywords\": [\n        \"laravel\",\n        \"optimus\",\n        \"hashids\",\n        \"fakeid\",\n        \"obfuscation\"\n    ],\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n            \"name\": \"Propaganistas\",\n            \"email\": \"Propaganistas@users.noreply.github.com\"\n        }\n    ],\n    \"require\": {\n        \"php\": \"^8.0\",\n        \"illuminate/config\": \"^9.0|^10.0\",\n        \"illuminate/container\": \"^9.0|^10.0\",\n        \"illuminate/routing\": \"^9.0|^10.0\",\n        \"illuminate/support\": \"^9.0|^10.0\",\n        \"jenssegers/optimus\": \"^1.0\"\n    },\n    \"require-dev\": {\n        \"orchestra/testbench\": \"*\",\n        \"phpunit/phpunit\": \"^9.5.10\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"Propaganistas\\\\LaravelFakeId\\\\\": \"src/\"\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Propaganistas\\\\LaravelFakeId\\\\Tests\\\\\": \"tests/\"\n        }\n    },\n    \"extra\": {\n        \"laravel\": {\n            \"providers\": [\n                \"Propaganistas\\\\LaravelFakeId\\\\FakeIdServiceProvider\"\n            ],\n            \"aliases\": {\n                \"FakeId\": \"Propaganistas\\\\LaravelFakeId\\\\Facades\\\\FakeId\"\n            }\n        }\n    },\n    \"minimum-stability\": \"dev\",\n    \"prefer-stable\": true,\n    \"suggest\": {\n        \"vinkla/hashids\": \"Laravel-FakeId is deprecated\"\n    }\n}\n"
  },
  {
    "path": "config/config.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | FakeId connection settings\n    |--------------------------------------------------------------------------\n    |\n    | Since FakeId depends on jenssegers/optimus, we need three values:\n    | - A large prime number lower than 2147483647\n    | - The inverse prime so that (PRIME * INVERSE) & MAXID == 1\n    | - A large random integer lower than 2147483647\n    |\n    | Run the `fakeid:setup` Artisan command to auto-generate random values.\n    |\n    */\n\n    'prime'   => env('FAKEID_PRIME', 961748927),\n    'inverse' => env('FAKEID_INVERSE', 1430310975),\n    'random'  => env('FAKEID_RANDOM', 620464665),\n\n];\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" backupGlobals=\"false\" backupStaticAttributes=\"false\" bootstrap=\"vendor/autoload.php\" colors=\"true\" convertErrorsToExceptions=\"true\" convertNoticesToExceptions=\"true\" convertWarningsToExceptions=\"true\" processIsolation=\"false\" stopOnFailure=\"false\" xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/9.3/phpunit.xsd\">\n  <coverage includeUncoveredFiles=\"true\">\n    <include>\n      <directory suffix=\".php\">./src</directory>\n    </include>\n    <exclude>\n      <file>./src/Commands/FakeIdSetupCommand.php</file>\n    </exclude>\n  </coverage>\n  <testsuites>\n    <testsuite name=\"Package Test Suite\">\n      <directory suffix=\".php\">./tests/</directory>\n    </testsuite>\n  </testsuites>\n</phpunit>\n"
  },
  {
    "path": "src/Commands/FakeIdSetupCommand.php",
    "content": "<?php\n\nnamespace Propaganistas\\LaravelFakeId\\Commands;\n\nuse Illuminate\\Support\\Str;\nuse Illuminate\\Console\\Command;\nuse Jenssegers\\Optimus\\Energon;\n\nclass FakeIdSetupCommand extends Command\n{\n    /**\n     * The name and signature of the console command.\n     *\n     * @var string\n     */\n    protected $signature = 'fakeid:setup\n                            {--o|overwrite : Silently overwrite existing configuration}\n                            {--p|preserve : Silently preserves existing configuration}';\n\n    /**\n     * The console command description.\n     *\n     * @var string\n     */\n    protected $description = 'Configures FakeId for use';\n\n    /**\n     * Execute the console command.\n     *\n     * @return void\n     */\n    public function handle()\n    {\n        // Write in environment file.\n        $path = base_path('.env');\n        $env = file($path);\n\n        // Detect existing configuration.\n        if ($this->hasExistingConfiguration($env)) {\n\n            if ($this->option('preserve')) {\n                return;\n            }\n\n            if (! $this->option('overwrite')) {\n                if (! $this->confirm(\"Overwrite existing configuration?\")) {\n                    return;\n                }\n            }\n\n            $this->removeExistingConfiguration($env);\n        }\n\n        $this->writeNewConfiguration($env, $path);\n        $this->info(\"FakeId configured correctly.\");\n    }\n\n    /**\n     * Checks if the given file array contains existing FakeId configuration.\n     */\n    protected function hasExistingConfiguration($file)\n    {\n        return Str::contains(implode(' ', $file), 'FAKEID_');\n    }\n\n    /**\n     * Removes existing FakeId configuration from the given file array.\n     *\n     * @param array $file\n     */\n    protected function removeExistingConfiguration(&$file)\n    {\n        foreach ($file as $k => $line) {\n            if (strpos($line, 'FAKEID_') === 0) {\n                unset($file[$k]);\n            }\n        }\n    }\n\n    /**\n     * Writes new configuration using the provided file array to the given path.\n     *\n     * @param array $file\n     * @param string $path\n     */\n    protected function writeNewConfiguration($file, $path)\n    {\n        list($prime, $inverse, $rand) = Energon::generate();\n\n        $file[] = \"\\nFAKEID_PRIME=\" . $prime;\n        $file[] = \"\\nFAKEID_INVERSE=\" . $inverse;\n        $file[] = \"\\nFAKEID_RANDOM=\" . $rand;\n\n        file_put_contents($path, implode('', $file), LOCK_EX);\n    }\n}\n"
  },
  {
    "path": "src/Facades/FakeId.php",
    "content": "<?php namespace Propaganistas\\LaravelFakeId\\Facades;\n\nuse Illuminate\\Support\\Facades\\Facade;\n\nclass FakeId extends Facade\n{\n    /**\n     * Get the registered name of the component.\n     *\n     * @return string\n     * @throws \\RuntimeException\n     */\n    protected static function getFacadeAccessor()\n    {\n        return 'fakeid';\n    }\n\n}\n"
  },
  {
    "path": "src/FakeIdServiceProvider.php",
    "content": "<?php namespace Propaganistas\\LaravelFakeId;\n\nuse Illuminate\\Support\\ServiceProvider;\nuse Jenssegers\\Optimus\\Optimus;\nuse Propaganistas\\LaravelFakeId\\Commands\\FakeIdSetupCommand;\n\nclass FakeIdServiceProvider extends ServiceProvider\n{\n    /**\n     * Boots the service provider.\n     *\n     * @return void\n     */\n    public function boot()\n    {\n        // Publish config.\n        $this->publishes([\n            __DIR__ . '/../config/config.php' => config_path('fakeid.php'),\n        ], 'config');\n    }\n\n    /**\n     * Register the service provider.\n     *\n     * @return void\n     */\n    public function register()\n    {\n        $this->mergeConfigFrom(__DIR__ . '/../config/config.php', 'fakeid');\n\n        $this->registerCommand();\n        $this->registerOptimus();\n    }\n\n    /**\n     * Register the Optimus container.\n     *\n     * @return void\n     */\n    protected function registerOptimus()\n    {\n        $this->app->singleton('Jenssegers\\Optimus\\Optimus', function ($app) {\n            return new Optimus(\n                $app['config']['fakeid.prime'],\n                $app['config']['fakeid.inverse'],\n                $app['config']['fakeid.random']\n            );\n        });\n\n        $this->app->alias('Jenssegers\\Optimus\\Optimus', 'optimus');\n        $this->app->alias('Jenssegers\\Optimus\\Optimus', 'fakeid');\n    }\n\n    /**\n     * Register the Artisan setup command.\n     *\n     * @return void\n     */\n    protected function registerCommand()\n    {\n        $this->app->singleton('fakeid.command.setup', function ($app) {\n            return new FakeIdSetupCommand;\n        });\n\n        $this->commands('fakeid.command.setup');\n    }\n}\n"
  },
  {
    "path": "src/RoutesWithFakeIds.php",
    "content": "<?php namespace Propaganistas\\LaravelFakeId;\n\nuse Illuminate\\Support\\Facades\\App;\nuse Exception;\nuse RuntimeException;\n\ntrait RoutesWithFakeIds\n{\n    /**\n     * Get the value of the model's route key.\n     *\n     * @return mixed\n     */\n    public function getRouteKey()\n    {\n        $key = $this->getKey();\n\n        if ($this->getKeyType() === 'int' && (is_int($key) || ctype_digit($key))) {\n            return App::make('fakeid')->encode((int) $key);\n        }\n\n        throw new RuntimeException('Key should be of type int to encode into a fake id.');\n    }\n\n    /**\n     * Retrieve the model for a bound value.\n     *\n     * @param  \\Illuminate\\Database\\Eloquent\\Model|\\Illuminate\\Database\\Eloquent\\Relations\\Relation  $query\n     * @param  mixed  $value\n     * @param  string|null  $field\n     * @return \\Illuminate\\Database\\Eloquent\\Builder\n     */\n    public function resolveRouteBindingQuery($query, $value, $field = null)\n    {\n        if (ctype_digit($value) || is_int($value)) {\n            try {\n                $value = App::make('fakeid')->decode((int) $value);\n            } catch (Exception $e) {}\n        }\n\n        return $query->where($field ?? $this->getRouteKeyName(), $value);\n    }\n}\n"
  },
  {
    "path": "tests/Entities/Deletable.php",
    "content": "<?php\nnamespace Propaganistas\\LaravelFakeId\\Tests\\Entities;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\SoftDeletes;\nuse Propaganistas\\LaravelFakeId\\RoutesWithFakeIds;\n\nclass Deletable extends Model\n{\n    use SoftDeletes;\n    use RoutesWithFakeIds;\n}"
  },
  {
    "path": "tests/Entities/Fake.php",
    "content": "<?php\nnamespace Propaganistas\\LaravelFakeId\\Tests\\Entities;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Propaganistas\\LaravelFakeId\\RoutesWithFakeIds;\n\nclass Fake extends Model\n{\n    use RoutesWithFakeIds;\n}"
  },
  {
    "path": "tests/Entities/FakeWithRouteKeyName.php",
    "content": "<?php\nnamespace Propaganistas\\LaravelFakeId\\Tests\\Entities;\n\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Propaganistas\\LaravelFakeId\\RoutesWithFakeIds;\n\nclass FakeWithRouteKeyName extends Model\n{\n    use RoutesWithFakeIds;\n\n    protected $table = \"fakes\";\n\n    public function getRouteKeyName()\n    {\n        return 'foo';\n    }\n}"
  },
  {
    "path": "tests/Entities/Real.php",
    "content": "<?php\nnamespace Propaganistas\\LaravelFakeId\\Tests\\Entities;\n\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass Real extends Model\n{\n\n}"
  },
  {
    "path": "tests/FakeIdTest.php",
    "content": "<?php namespace Propaganistas\\LaravelFakeId\\Tests;\n\nuse Illuminate\\Database\\Capsule\\Manager as DB;\nuse Illuminate\\Database\\Eloquent\\ModelNotFoundException;\nuse Illuminate\\Routing\\Middleware\\SubstituteBindings;\nuse Illuminate\\Support\\Facades\\Route;\nuse Orchestra\\Testbench\\TestCase;\nuse Propaganistas\\LaravelFakeId\\Facades\\FakeId;\nuse Propaganistas\\LaravelFakeId\\FakeIdServiceProvider;\nuse Propaganistas\\LaravelFakeId\\Tests\\Entities\\Fake;\nuse Propaganistas\\LaravelFakeId\\Tests\\Entities\\FakeWithRouteKeyName;\nuse Propaganistas\\LaravelFakeId\\Tests\\Entities\\Real;\nuse Propaganistas\\LaravelFakeId\\Tests\\Entities\\Deletable;\nuse RuntimeException;\n\nclass FakeIdTest extends TestCase\n{\n    protected function getPackageProviders($application)\n    {\n        return [\n            FakeIdServiceProvider::class,\n        ];\n    }\n\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->configureDatabase();\n    }\n\n    protected function configureDatabase()\n    {\n        $db = new DB;\n        $db->addConnection([\n            'driver'    => 'sqlite',\n            'database'  => ':memory:',\n            'charset'   => 'utf8',\n            'collation' => 'utf8_unicode_ci',\n            'prefix'    => '',\n        ]);\n        $db->bootEloquent();\n        $db->setAsGlobal();\n\n        DB::schema()->create('reals', function ($table) {\n            $table->increments('id');\n            $table->timestamps();\n        });\n\n        DB::schema()->create('fakes', function ($table) {\n            $table->increments('id');\n            $table->timestamps();\n        });\n\n        DB::schema()->create('deletables', function ($table) {\n            $table->increments('id');\n            $table->timestamps();\n            $table->softDeletes();\n        });\n    }\n\n    protected function createRoute($path, $handler)\n    {\n        return $this->app['router']->get($path, [\n            'middleware' => SubstituteBindings::class,\n            'uses' => $handler,\n        ]);\n    }\n\n    /** @test */\n    public function it_resolves_the_facade()\n    {\n        $this->assertInstanceOf('Jenssegers\\Optimus\\Optimus', FakeId::getFacadeRoot());\n    }\n\n    /** @test */\n    public function it_encodes_the_route_key()\n    {\n        $model = Fake::create();\n\n        $this->assertNotEquals($model->getRouteKey(), $model->getKey());\n        $this->assertEquals($model->getRouteKey(), app('fakeid')->encode($model->getKey()));\n    }\n\n    /** @test */\n    public function it_decodes_the_route_key_when_resolving()\n    {\n        $model = Fake::create();\n\n        $query = $model->resolveRouteBindingQuery(Fake::query(), $model->getRouteKey());\n\n        $this->assertNotEquals($model->getRouteKey(), $model->getKey());\n        $this->assertEquals('select * from \"fakes\" where \"id\" = ?', $query->toSql());\n        $this->assertEquals([$model->getKey()], $query->getBindings());\n    }\n\n    /** @test */\n    public function it_decodes_the_route_key_when_resolving_with_the_custom_route_key_name()\n    {\n        $model = FakeWithRouteKeyName::create();\n\n        $query = $model->resolveRouteBindingQuery(Fake::query(), $model->getRouteKey());\n\n        $this->assertNotEquals($model->getRouteKey(), $model->getKey());\n        $this->assertEquals('select * from \"fakes\" where \"foo\" = ?', $query->toSql());\n        $this->assertEquals([$model->getKey()], $query->getBindings());\n    }\n\n    /** @test */\n    public function it_decodes_the_route_key_when_resolving_with_a_custom_attribute()\n    {\n        $model = Fake::create();\n\n        $query = $model->resolveRouteBindingQuery(Fake::query(), $model->getRouteKey(), 'foo');\n\n        $this->assertNotEquals($model->getRouteKey(), $model->getKey());\n        $this->assertEquals('select * from \"fakes\" where \"foo\" = ?', $query->toSql());\n        $this->assertEquals([$model->getKey()], $query->getBindings());\n    }\n\n    /** @test */\n    public function it_doesnt_throw_when_resolving_an_undecodable_route_key()\n    {\n        $model = Fake::create();\n\n        $query = $model->resolveRouteBindingQuery(Fake::query(), 'abc');\n\n        $this->assertNotEquals($model->getRouteKey(), $model->getKey());\n        $this->assertEquals('select * from \"fakes\" where \"id\" = ?', $query->toSql());\n        $this->assertEquals(['abc'], $query->getBindings());\n    }\n\n    /** @test */\n    public function it_resolves_implicit_bindings()\n    {\n        $this->createRoute('fake/{fake}', function (Fake $fake) {\n            return \"ID:{$fake->getKey()}\";\n        });\n\n        $model = Fake::create();\n\n        $response = $this->get(\"fake/{$model->getRouteKey()}\");\n\n        $this->assertEquals(200, $response->getStatusCode());\n        $this->assertEquals(\"ID:{$model->getKey()}\", $response->getContent());\n    }\n\n    /** @test */\n    public function it_resolves_implicit_bindings_with_trashed()\n    {\n        $this->createRoute('fake/{deletable}', function (Deletable $deletable) {\n            return \"ID:{$deletable->getKey()}\";\n        })->withTrashed();\n\n        $model = Deletable::create();\n        $model->delete();\n\n        $response = $this->get(\"fake/{$model->getRouteKey()}\");\n\n        $this->assertEquals(200, $response->getStatusCode());\n        $this->assertEquals(\"ID:{$model->getKey()}\", $response->getContent());\n    }\n\n    /** @test */\n    public function it_resolves_explicit_bindings()\n    {\n        Route::model('fake', Fake::class);\n\n        $this->createRoute('fake/{fake}', function (Fake $fake) {\n            return \"ID:{$fake->getKey()}\";\n        });\n\n        $model = Fake::create();\n\n        $response = $this->get(\"fake/{$model->getRouteKey()}\");\n\n        $this->assertEquals(200, $response->getStatusCode());\n        $this->assertEquals(\"ID:{$model->getKey()}\", $response->getContent());\n    }\n\n    /**\n     * Explicit model bindings completely omit a model's route resolution logic.\n     * `$route->withTrashed()` only works for implicit bindings, so it won't\n     * help us here. There's no real way to support this feature properly.\n     *\n     * This test solely exists to remind us all of that :-)\n     *\n     * Or if Laravel implements `withTrashed()` support for explicit\n     * bindings some time, it will notify us by simply failing.\n     *\n     * See next test for a working explicit binding callback.\n     *\n     * @test\n     */\n    public function it_cannot_resolve_soft_deleted_explicit_bindings_with_trashed()\n    {\n        Route::model('deletable', Deletable::class);\n\n        $this->createRoute('fake/{deletable}', function (Deletable $deletable) {\n            return \"ID:{$deletable->getKey()}\";\n        })->withTrashed(); // Has NO effect for explicit bindings.\n\n        $model = Deletable::create();\n        $model->delete();\n\n        $response = $this->get(\"fake/{$model->getRouteKey()}\");\n\n        $this->assertEquals(404, $response->getStatusCode());\n        $this->assertNotNull($response->exception);\n        $this->assertEquals(ModelNotFoundException::class, get_class($response->exception));\n    }\n\n    /**\n     * This test solely exists to provide a working boilerplate\n     * to showcase how explicit bindings could be configured\n     * to properly work with soft-deleted models.\n     *\n     * @test\n     */\n    public function it_resolves_soft_deleted_explicit_bindings_with_trashed_with_working_callback()\n    {\n        // This is the important part.\n        Route::model('deletable', Deletable::class, function ($value) {\n            $query = Deletable::query()->withTrashed();\n            return (new Deletable)->resolveRouteBindingQuery($query, $value)->firstOrFail();\n        });\n\n        $this->createRoute('fake/{deletable}', function (Deletable $deletable) {\n            return \"ID:{$deletable->getKey()}\";\n        })->withTrashed(); // Has NO effect for explicit bindings.\n\n        $model = Deletable::create();\n        $model->delete();\n\n        $response = $this->get(\"fake/{$model->getRouteKey()}\");\n\n        $this->assertEquals(200, $response->getStatusCode());\n        $this->assertEquals(\"ID:{$model->getKey()}\", $response->getContent());\n    }\n\n    /** @test */\n    public function it_returns_notfound_on_model_not_found()\n    {\n        $this->createRoute('fake/{fake}', function (Fake $fake) {\n            return \"ID:{$fake->getKey()}\";\n        });\n\n        $model = Fake::create();\n        $model->delete();\n\n        $response = $this->get(\"fake/{$model->getRouteKey()}\");\n\n        $this->assertEquals(404, $response->getStatusCode());\n        $this->assertNotNull($response->exception);\n        $this->assertEquals(ModelNotFoundException::class, get_class($response->exception));\n    }\n\n    /** @test */\n    public function it_returns_notfound_on_undecodable_route_key()\n    {\n        $this->createRoute('fake/{fake}', function (Fake $fake) {\n            return \"ID:{$fake->getKey()}\";\n        });\n\n        $response = $this->get('fake/foo');\n\n        $this->assertEquals(404, $response->getStatusCode());\n        $this->assertNotNull($response->exception);\n        $this->assertEquals(ModelNotFoundException::class, get_class($response->exception));\n    }\n}"
  }
]