diff --git a/src/APIResourceManager.php b/src/APIResourceManager.php index ac53e49..a48f64d 100644 --- a/src/APIResourceManager.php +++ b/src/APIResourceManager.php @@ -3,41 +3,33 @@ namespace Juampi92\APIResources; use Exception; -use Illuminate\Support\Str; -use Juampi92\APIResources\Exceptions\ResourceNotFoundException; -use Juampi92\APIResources\Resolvers\ResolverFactory; +use Illuminate\Contracts\Config\Repository; use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\ResourceCollection; +use Juampi92\APIResources\Resolvers\ClassnameResolverFactory; use Juampi92\APIResources\Support\Version; class APIResourceManager { - protected string $current; + private Repository $config; - protected string $latest; + private ClassnameResolverFactory $classnameResolverFactory; - /** - * @var string - */ - protected $path; + private ?APIResourceVersion $version = null; - /** - * @var string - */ - protected $apiName; - - /** - * @var string - */ - protected $resources; - - /** - * @var string|null - */ - protected $routePath; + public function __construct(Repository $config, ClassnameResolverFactory $classnameResolverFactory) + { + $this->config = $config; + $this->classnameResolverFactory = $classnameResolverFactory; + } - public function __construct() + private function getResourceVersion(): APIResourceVersion { + if (! $this->version) { + throw new Exception('APIResource not initialised. Please use APIResource::setVersion($version) to specify the current version'); + } + + return $this->version; } /** @@ -46,17 +38,9 @@ public function __construct() * @param string $route * @return string */ - public function getRouteName($route) + public function getRouteName(string $route) { - if (! $this->routePath) { - // Grab route_prefix config first. If it's not set, - // grab the resources, and replace `\` with `.`, and - // transform it all to lowercase. - $this->routePath = $this->getConfig('route_prefix') - ?: str_replace('\\', '.', strtolower($this->getConfig('resources'))); - } - - return "{$this->routePath}.v{$this->current}" . Str::after($route, $this->routePath); + return $this->getResourceVersion()->getRouteName($route); } /** @@ -70,25 +54,6 @@ public function getRoute($name, $parameters = [], bool $absolute = true): string return route($this->getRouteName((string) $name), $parameters, $absolute); } - /** - * Get config considering the API name if present. - * - * @param string $cfg Config path - * @param string $name Name of api if present - * - * @return mixed The result of the config - */ - protected function getConfig($cfg, $name = null) - { - if (is_null($name)) { - $name = $this->apiName; - } - - $name = $name ? ".$name" : ''; - - return config("api.$cfg{$name}"); - } - /** * Sets the current API version. * @@ -97,28 +62,12 @@ protected function getConfig($cfg, $name = null) * * @return $this */ - public function setVersion(string $current, $apiName = null) + public function setVersion(string $current, ?string $apiName = null) { - $this->apiName = $apiName; - - $latestVersion = $this->getConfig('version'); - - if (! $latestVersion) { - throw new Exception('You must define a config(\'api\') with a latest version. Do: php artisan vendor:publish --provider="Juampi92/APIResources/APIResourcesServiceProvider"'); - } - - // Reset pre-cached properties - $this->current = $current; - $this->routePath = null; - $this->latest = $this->getConfig('version'); - - // Path can be only one or one for each api - $this->path = config('api.resources_path'); - if (is_array($this->path)) { - $this->path = $this->getConfig('resources_path'); - } + $config = new Config($this->config, $apiName); + $resolver = $this->classnameResolverFactory->make($config); - $this->resources = $this->getConfig('resources'); + $this->version = new APIResourceVersion($config, $current, $resolver); return $this; } @@ -130,12 +79,12 @@ public function setVersion(string $current, $apiName = null) */ public function getVersion() { - return $this->current; + return $this->getResourceVersion()->getVersion(); } public function getLatestVersion(): string { - return $this->latest; + return $this->getResourceVersion()->getLatestVersion(); } /** @@ -147,21 +96,11 @@ public function getLatestVersion(): string */ public function isLatest(string $current = null): bool { - if (! isset($current)) { - $current = $this->current; + if (is_null($current)) { + $current = $this->getResourceVersion()->getVersion(); } - return $this->latest === $current; - } - - public function getBasePath(): string - { - return $this->path; - } - - public function getResourcesPath(): ?string - { - return $this->resources; + return $this->getResourceVersion()->getLatestVersion() === $current; } /** @@ -176,15 +115,17 @@ public function getResourcesPath(): ?string */ public function resolve(string $classname): APIResource { - return new APIResource(ResolverFactory::make($classname)->run()); + return new APIResource( + $this->resolveClassname($classname) + ); } /** - * @throws ResourceNotFoundException + * @returns class-string */ public function resolveClassname(string $classname): string { - return ResolverFactory::make($classname)->run(); + return $this->getResourceVersion()->resolveClassname($classname); } /** @@ -212,22 +153,4 @@ public function collection($classname, ...$args) return $resource->collection(...$args); } - - /** - * @return array - */ - public function getVersionsBetweenCurrentAndLatest(): array - { - return $this->getVersionsBetween($this->current, $this->latest); - } - - /** - * @return array - */ - private function getVersionsBetween(string $current, string $latest): array - { - $versions = $this->getConfig('versions') ?: [$current, $latest]; - - return Version::fromRange($versions, $current, $latest); - } } diff --git a/src/APIResourceVersion.php b/src/APIResourceVersion.php new file mode 100644 index 0000000..95fba80 --- /dev/null +++ b/src/APIResourceVersion.php @@ -0,0 +1,86 @@ +config = $config; + $this->version = $version; + $this->resolver = $resolver; + + if (! $this->getLatestVersion()) { + throw new Exception('You must define a config(\'api\') with a latest version. Do: php artisan vendor:publish --provider="Juampi92/APIResources/APIResourcesServiceProvider"'); + } + } + + /** + * Returns the name of the versioned route. + * + * @param string $route + * @return string + */ + public function getRouteName(string $route): string + { + if (! isset($this->routePath)) { + // Grab route_prefix config first. If it's not set, + // grab the resources, and replace `\` with `.`, and + // transform it all to lowercase. + $this->routePath = $this->config->get('route_prefix') + ?: str_replace('\\', '.', strtolower($this->config->get('resources'))); + } + + return "{$this->routePath}.v{$this->version}" . Str::after($route, $this->routePath); + } + + public function getVersion(): string + { + return $this->version; + } + + public function getLatestVersion(): string + { + return $this->config->get('version'); + } + + /** + * @return array + */ + private function getVersionsArray(): array + { + $current = $this->getVersion(); + $latest = $this->getLatestVersion(); + + $versions = $this->config->get('versions') ?: [$current, $latest]; + + return Version::fromRange($versions, $current, $latest); + } + + /** + * @param string $classname + * @return class-string + */ + public function resolveClassname(string $classname): string + { + return $this->resolver->resolve( + $classname, + $this->getVersionsArray() + ); + } +} diff --git a/src/APIResourcesServiceProvider.php b/src/APIResourcesServiceProvider.php index d2cebed..349b574 100644 --- a/src/APIResourcesServiceProvider.php +++ b/src/APIResourcesServiceProvider.php @@ -22,7 +22,7 @@ public function boot(): void public function register(): void { $this->app->singleton('apiresource', function () { - return new APIResourceManager(); + return resolve(APIResourceManager::class); }); } diff --git a/src/Config.php b/src/Config.php new file mode 100644 index 0000000..27636a6 --- /dev/null +++ b/src/Config.php @@ -0,0 +1,43 @@ +config = $config; + $this->name = $name; + } + + /** + * @param string $key + * @param mixed $default + * @return mixed + */ + public function get(string $key, $default = null) + { + $fullKey = $this->wrapInNamespace($key); + + return $this->config->get("api.{$fullKey}", $default); + } + + private function wrapInNamespace(string $key): string + { + if (! $this->name) { + return $key; + } + + // When the key itself is not an array, we don't need the namespace. + if (! is_array($this->config->get($key))) { + return $key; + } + + return "{$this->name}.$key"; + } +} diff --git a/src/Resolvers/CacheClassnameResolver.php b/src/Resolvers/CacheClassnameResolver.php new file mode 100644 index 0000000..e23102a --- /dev/null +++ b/src/Resolvers/CacheClassnameResolver.php @@ -0,0 +1,40 @@ +storage = $storage; + + $this->routingManifest = include_once $this->storage->path( + $config->get('routing_cache_path') + ); + } + + public function resolve(string $classname, array $versions): string + { + $currentVersion = Arr::first($versions); + + /** @var class-string|null $path */ + $path = $this->routingManifest[$classname][$currentVersion] ?? null; + + if (! class_exists($path)) { + throw new ResourceNotFoundException($classname, last($versions)); + } + + return $path; + } +} diff --git a/src/Resolvers/CacheResolver.php b/src/Resolvers/CacheResolver.php deleted file mode 100644 index 8e61c74..0000000 --- a/src/Resolvers/CacheResolver.php +++ /dev/null @@ -1,35 +0,0 @@ - $path */ - $path = $routingManifest[$this->classname][$version] ?? null; - - if (! $path) { - continue; - } - - if (class_exists($path)) { - return $path; - } - } - - throw new ResourceNotFoundException($this->classname, APIResource::getLatestVersion()); - } -} diff --git a/src/Resolvers/ClassnameResolver.php b/src/Resolvers/ClassnameResolver.php new file mode 100644 index 0000000..3e8c8e7 --- /dev/null +++ b/src/Resolvers/ClassnameResolver.php @@ -0,0 +1,14 @@ + $versions + * @return class-string + */ + public function resolve(string $classname, array $versions): string; +} diff --git a/src/Resolvers/ClassnameResolverFactory.php b/src/Resolvers/ClassnameResolverFactory.php new file mode 100644 index 0000000..2399e35 --- /dev/null +++ b/src/Resolvers/ClassnameResolverFactory.php @@ -0,0 +1,18 @@ + $config]); + } + + return resolve(PathClassnameResolver::class, ['config' => $config]); + } +} diff --git a/src/Resolvers/PathClassnameResolver.php b/src/Resolvers/PathClassnameResolver.php new file mode 100644 index 0000000..96e2610 --- /dev/null +++ b/src/Resolvers/PathClassnameResolver.php @@ -0,0 +1,50 @@ +config = $config; + } + + public function resolve(string $classname, array $versions): string + { + $basePath = $this->config->get('base_path'); + + foreach ($versions as $version) { + $version = Str::start($version, 'v'); + + $path = "\\{$basePath}\\" . $this->guessResourcePath($classname, $version); + + if (class_exists($path)) { + return $path; + } + } + + throw new ResourceNotFoundException($classname, last($versions)); + } + + private function guessResourcePath(string $classname, string $version): string + { + $resourcesPath = $this->config->get('resources_path'); + + if (empty($resourcesPath)) { + return "{$version}\\{$classname}"; + } + + return sprintf( + "%s\\%s\\%s", + $resourcesPath, + $version, + Str::after($classname, $resourcesPath . "\\") + ); + } +} diff --git a/src/Resolvers/PathResolver.php b/src/Resolvers/PathResolver.php deleted file mode 100644 index 9d84f5b..0000000 --- a/src/Resolvers/PathResolver.php +++ /dev/null @@ -1,56 +0,0 @@ -getPath($version); - - // Check if the resource was found - if (class_exists($path)) { - return $path; - } - } - - throw new ResourceNotFoundException($this->classname, APIResource::getLatestVersion()); - } - - private function guessResourcePath(string $version): string - { - $resourcesPath = APIResource::getResourcesPath(); - - if (empty($resourcesPath)) { - return "{$version}\\{$this->classname}"; - } - - return sprintf( - "%s\\%s\\%s", - $resourcesPath, - $version, - Str::after($this->classname, $resourcesPath . "\\") - ); - } - - /** - * @return class-string - */ - protected function getPath($version): string - { - $basePath = APIResource::getBasePath(); - - $resourcePath = $this->guessResourcePath($version); - - return "\\{$basePath}\\{$resourcePath}"; - } -} diff --git a/src/Resolvers/Resolver.php b/src/Resolvers/Resolver.php deleted file mode 100644 index 18022f7..0000000 --- a/src/Resolvers/Resolver.php +++ /dev/null @@ -1,23 +0,0 @@ -classname = $classname; - } - - /** - * @return class-string - * @throws ResourceNotFoundException - */ - abstract public function run(): string; -} diff --git a/src/Resolvers/ResolverFactory.php b/src/Resolvers/ResolverFactory.php deleted file mode 100644 index b033c8c..0000000 --- a/src/Resolvers/ResolverFactory.php +++ /dev/null @@ -1,19 +0,0 @@ - [ @@ -18,12 +18,12 @@ ], /* - |-------------------------------------------------------------------------- - | API Versions - |-------------------------------------------------------------------------- - | - | Here you have a list of versions ordered from oldest to latest. - */ + |-------------------------------------------------------------------------- + | API Versions + |-------------------------------------------------------------------------- + | + | Here you have a list of versions ordered from oldest to latest. + */ 'versions' => [ 'app' => [ @@ -40,33 +40,33 @@ ], /* - |-------------------------------------------------------------------------- - | API Default - |-------------------------------------------------------------------------- - | - | + |-------------------------------------------------------------------------- + | API Default + |-------------------------------------------------------------------------- + | + | */ 'default' => 'app', /* - |-------------------------------------------------------------------------- - | Resorces homepath - |-------------------------------------------------------------------------- - | - | This value is the base folder where your resources are stored. - | + |-------------------------------------------------------------------------- + | Resorces homepath + |-------------------------------------------------------------------------- + | + | This value is the base folder where your resources are stored. + | */ 'resources_path' => 'Juampi92\APIResources\Tests\Fixtures\Resources', /* - |-------------------------------------------------------------------------- - | Resorces - |-------------------------------------------------------------------------- - | - | Here is the folder that has versionated resources. If you store them - | in the root, leave this empty '' + |-------------------------------------------------------------------------- + | Resorces + |-------------------------------------------------------------------------- + | + | Here is the folder that has versionated resources. If you store them + | in the root, leave this empty '' */ 'resources' => [ @@ -76,12 +76,12 @@ ], /* - |-------------------------------------------------------------------------- - | Routing manifest file - |-------------------------------------------------------------------------- - | - | Here is the path for the routing manifest that will cache the resolution - | strategy for each Resource in every version. + |-------------------------------------------------------------------------- + | Resolution cache path + |-------------------------------------------------------------------------- + | + | Here is the path for the resolution manifest that will cache the resolution + | strategy for each Resource in every version. */ 'routing_cache_path' => app()->bootstrapPath('cache/api-resources-cache.php'), diff --git a/tests/Fixtures/config/simple.php b/tests/Fixtures/config/simple.php index 88d7867..e02274c 100644 --- a/tests/Fixtures/config/simple.php +++ b/tests/Fixtures/config/simple.php @@ -59,4 +59,4 @@ */ 'routing_cache_path' => app()->bootstrapPath('cache/api-resources-cache.php'), -]; + ];