Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/docs/3.0/repository-pattern/repository-pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,38 @@ class Post extends Repository

:::

## Repository prefix

Restify generates the URI for the repository in the following way:

```php
config('restify.base') . '/' . UserRepository::uriKey() . '/'
```

For example, let's assume we have the `restify.base` equal with: `api/restify`, the default URI generated for the UserRepository is:

```http request
GET: /api/restify/users
```

However, you can prefix the repository with your own:

```php
// UserRepository
public static $prefix = 'api/v1';
```

Now, the generated URI will look like this:

```http request
GET: /api/v1/users
```

:::tip
For the rest of the repositories the prefix will stay as it is, the default one.

Keep in mind that this custom prefix, will be used for all the endpoints related to the user repository.
:::

## Repository middleware

Expand Down Expand Up @@ -712,3 +744,5 @@ You can handle the repository boot, by using the `booted` static method:
````




12 changes: 9 additions & 3 deletions src/Http/Middleware/RestifyInjector.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class RestifyInjector
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
Expand All @@ -27,7 +27,13 @@ public function handle($request, Closure $next)

$isRestify = $request->is($path) ||
$request->is(trim($path.'/*', '/')) ||
$request->is('restify-api/*');
$request->is('restify-api/*') ||
collect(Restify::$repositories)
->filter(fn ($repository) => $repository::prefix())
->some(fn ($repository) => $request->is($repository::prefix().'/*')) ||
collect(Restify::$repositories)
->filter(fn ($repository) => $repository::indexPrefix())
->some(fn ($repository) => $request->is($repository::indexPrefix().'/*'));

app()->register(RestifyCustomRoutesProvider::class);

Expand Down
8 changes: 7 additions & 1 deletion src/Http/Requests/InteractWithRepositories.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,17 @@ public function repository($key = null): ?Repository
}

if (! $repository::authorizedToUseRepository($this)) {
throw new UnauthorizedException(__('Unauthorized to view repository :name. See "allowRestify" policy.', [
throw new UnauthorizedException(__('Unauthorized to view repository :name. Check "allowRestify" policy.', [
'name' => $repository,
]), 403);
}

if (! $repository::authorizedToUseRoute($this)) {
abort(403, __('Unauthorized to use the route :name. Check prefix.', [
'name' => $this->getRequestUri(),
]));
}

app(Pipeline::class)
->send($this)
->through(optional($repository::collectMiddlewares($this))->toArray())
Expand Down
3 changes: 2 additions & 1 deletion src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ abstract class Repository implements RestifySearchable, JsonSerializable
ConditionallyLoadsAttributes,
DelegatesToResource,
ResolvesActions,
RepositoryEvents;
RepositoryEvents,
WithRoutePrefix;

/**
* This is named `resource` because of the forwarding properties from DelegatesToResource trait.
Expand Down
78 changes: 78 additions & 0 deletions src/Repositories/WithRoutePrefix.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace Binaryk\LaravelRestify\Repositories;

use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Illuminate\Support\Str;

trait WithRoutePrefix
{
/**
* The repository routes default prefix.
*
* @var string
*/
public static $prefix;

/**
* The repository index route default prefix.
* @var string
*/
public static $indexPrefix;

public static function prefix(): ?string
{
return static::sanitizeSlashes(
static::$prefix
);
}

public static function indexPrefix(): ?string
{
return static::sanitizeSlashes(
static::$indexPrefix
);
}

protected static function sanitizeSlashes(?string $prefix): ?string
{
if ($prefix && Str::startsWith($prefix, '/')) {
$prefix = Str::replaceFirst('/', '', $prefix);
}

if ($prefix && Str::endsWith($prefix, '/')) {
$prefix = Str::replaceLast('/', '', $prefix);
}

return $prefix;
}

public static function authorizedToUseRoute(RestifyRequest $request): bool
{
if (! static::shouldAuthorizeRouteUsage()) {
return true;
}

if ($request->isForRepositoryRequest()) {
// index
if (static::indexPrefix()) {
return $request->is(static::indexPrefix().'/*');
}

if (static::prefix()) {
return $request->is(static::prefix().'/*');
}
} else {
// the rest
return $request->is(static::prefix().'/*');
}
}

protected static function shouldAuthorizeRouteUsage(): bool
{
return collect([
static::prefix(),
static::indexPrefix(),
])->some(fn ($prefix) => (bool) $prefix);
}
}
37 changes: 36 additions & 1 deletion src/RestifyServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Binaryk\LaravelRestify;

use Binaryk\LaravelRestify\Http\Controllers\RepositoryIndexController;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
Expand Down Expand Up @@ -35,7 +36,9 @@ protected function registerRoutes()
'middleware' => config('restify.middleware', []),
];

$this->defaultRoutes($config);
$this->defaultRoutes($config)
->registerPrefixed($config)
->registerIndexPrefixed($config);
}

/**
Expand All @@ -51,6 +54,38 @@ public function defaultRoutes($config)
return $this;
}

/**
* @param $config
* @return $this
*/
public function registerPrefixed($config)
{
collect(Restify::$repositories)
->filter(fn ($repository) => $repository::prefix())
->each(function ($repository) use ($config) {
$config['prefix'] = $repository::prefix();
Route::group($config, function () {
$this->loadRoutesFrom(__DIR__.'/../routes/api.php');
});
});

return $this;
}

public function registerIndexPrefixed($config)
{
collect(Restify::$repositories)
->filter(fn ($repository) => $repository::indexPrefix())
->each(function ($repository) use ($config) {
$config['prefix'] = $repository::indexPrefix();
Route::group($config, function () {
Route::get('/{repository}', '\\'.RepositoryIndexController::class);
});
});

return $this;
}

/**
* Register Restify's custom exception handler.
*
Expand Down
3 changes: 0 additions & 3 deletions tests/Fixtures/User/UserRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
use Binaryk\LaravelRestify\Repositories\Repository;
use Binaryk\LaravelRestify\Repositories\UserProfile;

/**
* @author Eduard Lupacescu <eduard.lupacescu@binarcode.com>
*/
class UserRepository extends Repository
{
use UserProfile;
Expand Down
2 changes: 1 addition & 1 deletion tests/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ abstract class IntegrationTest extends TestCase

protected function setUp(): void
{
$this->loadRepositories();
parent::setUp();
DB::enableQueryLog();

Expand All @@ -52,7 +53,6 @@ protected function setUp(): void
$this->loadRoutes();
$this->withFactories(__DIR__.'/Factories');
$this->injectTranslator();
$this->loadRepositories();
$this->app->bind(ExceptionHandler::class, RestifyHandler::class);
}

Expand Down
36 changes: 36 additions & 0 deletions tests/Repositories/RepositoryCustomPrefixTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Repositories;

use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostRepository;
use Binaryk\LaravelRestify\Tests\IntegrationTest;

class RepositoryCustomPrefixTest extends IntegrationTest
{
protected function setUp(): void
{
PostRepository::$prefix = 'api/restify-api/v1';

PostRepository::$indexPrefix = 'api/restify-api/index';

parent::setUp();
}

public function test_repository_can_have_custom_prefix()
{
$this->getJson('api/restify-api/index/'.PostRepository::uriKey())
->assertSuccessful();
}

public function test_repository_prefix_block_default_route()
{
$this->getJson('/restify-api/'.PostRepository::uriKey())
->assertForbidden();

$this->getJson('api/restify-api/index/'.PostRepository::uriKey())
->assertSuccessful();

$this->postJson('/restify-api/'.PostRepository::uriKey())
->assertForbidden();
}
}