Skip to content

Commit

Permalink
Add route key trait to use Optimus in models (#6)
Browse files Browse the repository at this point in the history
* Add trait for implicit route model binding
  • Loading branch information
ivanvermeyen authored and Anton Komarev committed Oct 15, 2017
1 parent a256b7c commit a146758
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 0 deletions.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ This facade will dynamically pass static method calls to the `optimus` object in

This class contains no public methods of interest. This class should be added to the providers array in `config/app.php`. This class will setup ioc bindings.

#### Traits\OptimusEncodedRouteKey

This trait can be used in an Eloquent model to enable automatic route model binding. You can then type hint a model in a route closure or a controller and Laravel will try to find it based on the encoded ID.

### Examples

Here you can see an example of just how simple this package is to use. Out of the box, the default adapter is `main`. After you enter your authentication details in the config file, it will just work:
Expand Down Expand Up @@ -178,6 +182,47 @@ class Foo
app()->make('Foo')->bar(20);
```

#### Eloquent Model Trait

To enable implicit route model binding based on the encoded ID, all you need to do is [configure the prime numbers](#optimus-numbers-generation) and use the `OptimusEncodedRouteKey` trait in your model.

If you don't want to use the default Optimus connection, you can specify a custom connection by adding an `$optimusConnection` property to you model.

```php
use Cog\Laravel\Optimus\Traits\OptimusEncodedRouteKey;
use Illuminate\Database\Eloquent\Model;

class YourModel extends Model
{
use OptimusEncodedRouteKey;

protected $optimusConnection = 'custom'; // optional
}
```

Now you can type hint your model in a route closure or controller and Laravel will use the encoded ID to query the database.

Note that implicit route model binding requires Laravel's `SubstituteBindings` middleware, which is part of the `web` middleware group.

```php
Route::get('url/to/{model}', function (YourModel $model) {
// ...
})->middleware('web');
```

To generate URL's to these routes you can either get the encoded route key:

```php
$encodedId = $model->getRouteKey();
$url = url("url/to/{$encodedId}");
```

Or you can use named routes and pass it the model. Laravel will do the rest.

```php
$url = route('my.named.route', [$model]);
```

## Changelog

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
Expand Down
50 changes: 50 additions & 0 deletions src/Traits/OptimusEncodedRouteKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Cog\Laravel\Optimus\Traits;

use Cog\Laravel\Optimus\Facades\Optimus;

trait OptimusEncodedRouteKey
{
/**
* Get the value of the model's route key.
*
* @return mixed
*/
public function getRouteKey()
{
$id = parent::getRouteKey();

return $this->getOptimus()->encode($id);
}

/**
* Retrieve the model for a bound value.
*
* @param mixed $value
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value)
{
$id = $this->getOptimus()->decode($value);

return $this->where($this->getRouteKeyName(), '=', $id)->first();
}

/**
* Get the Optimus instance.
*
* @return \Cog\Laravel\Optimus\OptimusManager
*/
protected function getOptimus()
{
$connection = null;

if (property_exists($this, 'optimusConnection')) {
$connection = $this->optimusConnection;
}

return Optimus::connection($connection);
}
}
16 changes: 16 additions & 0 deletions tests/Stubs/Models/UserWithCustomOptimusConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Cog\Tests\Laravel\Optimus\Stubs\Models;

use Cog\Laravel\Optimus\Traits\OptimusEncodedRouteKey;
use Illuminate\Database\Eloquent\Model;

class UserWithCustomOptimusConnection extends Model
{
use OptimusEncodedRouteKey;

protected $optimusConnection = 'custom';

protected $table = 'users';
protected $guarded = [];
}
14 changes: 14 additions & 0 deletions tests/Stubs/Models/UserWithDefaultOptimusConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Cog\Tests\Laravel\Optimus\Stubs\Models;

use Cog\Laravel\Optimus\Traits\OptimusEncodedRouteKey;
use Illuminate\Database\Eloquent\Model;

class UserWithDefaultOptimusConnection extends Model
{
use OptimusEncodedRouteKey;

protected $table = 'users';
protected $guarded = [];
}
141 changes: 141 additions & 0 deletions tests/Traits/OptimusEncodedRouteKeyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

/*
* This file is part of Laravel Optimus.
*
* (c) Anton Komarev <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Cog\Tests\Laravel\Optimus\Traits;

use Cog\Laravel\Optimus\Facades\Optimus;
use Cog\Tests\Laravel\Optimus\AbstractTestCase;
use Cog\Tests\Laravel\Optimus\Stubs\Models\UserWithCustomOptimusConnection;
use Cog\Tests\Laravel\Optimus\Stubs\Models\UserWithDefaultOptimusConnection;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Route;

/**
* Class OptimusEncodedRouteKeyTest.
*
* @package Cog\Tests\Laravel\Optimus\Traits
*/
class OptimusEncodedRouteKeyTest extends AbstractTestCase
{
protected function setUp()
{
parent::setUp();

$this->loadLaravelMigrations(config('database.default'));
$this->configurePrimeNumbers();
}

public function testRouteKeyIsEncoded()
{
$user = $this->createUserWithDefaultOptimusConnection();
$encodedId = Optimus::encode($user->id);

$this->assertEquals($encodedId, $user->getRouteKey());
}

public function testOptimusConnectionCanBeConfigured()
{
$user = $this->createUserWithCustomOptimusConnection();
$routeKey = $user->getRouteKey();

$correctEncodedId = Optimus::connection('custom')->encode($user->id);
$incorrectEncodedId = Optimus::connection('main')->encode($user->id);

$this->assertEquals($correctEncodedId, $routeKey);
$this->assertNotEquals($incorrectEncodedId, $routeKey);
}

public function testResolveModelWithEncodedKey()
{
$user = $this->createUserWithDefaultOptimusConnection();
$encodedId = $user->getRouteKey();
$resolvedUser = $user->resolveRouteBinding($encodedId);

$this->assertEquals($user->id, $resolvedUser->id);
}

public function testEncodedKeyIsUsedForRouteModelBinding()
{
$user = $this->createUserWithDefaultOptimusConnection();
$encodedId = $user->getRouteKey();

Route::get('users/{user}', function (UserWithDefaultOptimusConnection $user) {
return $user;
})->middleware(SubstituteBindings::class);

$this->get("users/{$encodedId}")->assertJsonFragment(['id' => $user->id]);
}

public function testEncodedRouteKeyIsUsedWhenGeneratingNamedRouteUrls()
{
$user = $this->createUserWithDefaultOptimusConnection();
$encodedId = Optimus::encode($user->id);

Route::get('users/{user}', function () {
})->name('test.route');

$expectedUrl = "{$this->baseUrl}/users/{$encodedId}";
$generatedUrl = route('test.route', [$user]);

$this->assertEquals($expectedUrl, $generatedUrl);
}

/**
* Create a test user with default Optimus connection in the database.
*
* @return \Cog\Tests\Laravel\Optimus\Stubs\Models\UserWithDefaultOptimusConnection
*/
protected function createUserWithDefaultOptimusConnection()
{
return UserWithDefaultOptimusConnection::create([
'name' => 'Default Test User',
'email' => '[email protected]',
'password' => 'p4ssw0rd',
]);
}

/**
* Create a test user with custom Optimus connection in the database.
*
* @return \Cog\Tests\Laravel\Optimus\Stubs\Models\UserWithCustomOptimusConnection
*/
protected function createUserWithCustomOptimusConnection()
{
return UserWithCustomOptimusConnection::create([
'name' => 'Custom Test User',
'email' => '[email protected]',
'password' => 'p4ssw0rd',
]);
}

/**
* Configure some random prime numbers.
*
* @return void
*/
protected function configurePrimeNumbers()
{
config()->set('optimus.connections', [
'main' => [
'prime' => 1490261603,
'inverse' => 1573362507,
'random' => 1369544188,
],
'custom' => [
'prime' => 1770719809,
'inverse' => 1417283009,
'random' => 508877541,
],
]);
}
}

0 comments on commit a146758

Please sign in to comment.