Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
->in(__DIR__)
->exclude([
'src/Core/Bridge/Symfony/Maker/Resources/skeleton',
'src/Laravel/Console/Maker/Resources/skeleton',
'src/Laravel/config',
'tests/Fixtures/app/var',
'docs/guides',
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## v4.0.4

### Features

* [5c2cecfa4](https://github.com/api-platform/core/pull/6708/commits/5c2cecfa40fdf7b3db8f77f0f389d2a78d71a917) feat(laravel): add make provider and processor commands

## v4.0.3

### Bug fixes
Expand Down
6 changes: 5 additions & 1 deletion src/Laravel/ApiPlatformProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,11 @@ function (Application $app) {
});

if ($this->app->runningInConsole()) {
$this->commands([Console\InstallCommand::class]);
$this->commands([
Console\InstallCommand::class,
Console\Maker\MakeStateProcessorCommand::class,
Console\Maker\MakeStateProviderCommand::class,
]);
}
}

Expand Down
81 changes: 81 additions & 0 deletions src/Laravel/Console/Maker/AbstractMakeStateCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[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 ApiPlatform\Laravel\Console\Maker;

use ApiPlatform\Laravel\Console\Maker\Utils\AppServiceProviderTagger;
use ApiPlatform\Laravel\Console\Maker\Utils\StateTemplateGenerator;
use ApiPlatform\Laravel\Console\Maker\Utils\StateTypeEnum;
use ApiPlatform\Laravel\Console\Maker\Utils\SuccessMessageTrait;
use Illuminate\Console\Command;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Filesystem\Filesystem;

abstract class AbstractMakeStateCommand extends Command
{
use SuccessMessageTrait;

public function __construct(
private readonly Filesystem $filesystem,
private readonly StateTemplateGenerator $stateTemplateGenerator,
private readonly AppServiceProviderTagger $appServiceProviderTagger,
) {
parent::__construct();
}

/**
* @throws FileNotFoundException
*/
public function handle(): int
{
$stateName = $this->askForStateName();

$directoryPath = base_path('src/State/');
$this->filesystem->ensureDirectoryExists($directoryPath);

$filePath = $this->stateTemplateGenerator->getFilePath($directoryPath, $stateName);
if ($this->filesystem->exists($filePath)) {
$this->error(\sprintf('[ERROR] The file "%s" can\'t be generated because it already exists.', $filePath));

return self::FAILURE;
}

$this->stateTemplateGenerator->generate($filePath, $stateName, $this->getStateType());
if (!$this->filesystem->exists($filePath)) {
$this->error(\sprintf('[ERROR] The file "%s" could not be created.', $filePath));

return self::FAILURE;
}

$this->appServiceProviderTagger->addTagToServiceProvider($stateName, $this->getStateType());

$this->writeSuccessMessage($filePath, $this->getStateType());

return self::SUCCESS;
}

protected function askForStateName(): string
{
do {
$stateType = $this->getStateType()->name;
$stateName = $this->ask(\sprintf('Choose a class name for your state %s (e.g. <fg=yellow>AwesomeState%s</>)', mb_strtolower($stateType), mb_ucfirst($stateType)));
if (empty($stateName)) {
$this->error('[ERROR] This value cannot be blank.');
}
} while (empty($stateName));

return $stateName;
}

abstract protected function getStateType(): StateTypeEnum;
}
27 changes: 27 additions & 0 deletions src/Laravel/Console/Maker/MakeStateProcessorCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[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 ApiPlatform\Laravel\Console\Maker;

use ApiPlatform\Laravel\Console\Maker\Utils\StateTypeEnum;

final class MakeStateProcessorCommand extends AbstractMakeStateCommand
{
protected $signature = 'make:state-processor';
protected $description = 'Creates an API Platform state processor';

protected function getStateType(): StateTypeEnum
{
return StateTypeEnum::Processor;
}
}
27 changes: 27 additions & 0 deletions src/Laravel/Console/Maker/MakeStateProviderCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[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 ApiPlatform\Laravel\Console\Maker;

use ApiPlatform\Laravel\Console\Maker\Utils\StateTypeEnum;

final class MakeStateProviderCommand extends AbstractMakeStateCommand
{
protected $signature = 'make:state-provider';
protected $description = 'Creates an API Platform state provider';

protected function getStateType(): StateTypeEnum
{
return StateTypeEnum::Provider;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace {{ namespace }};

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;

final class {{ class_name }} implements ProcessorInterface
{
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void
{
// Handle the state
}
}
16 changes: 16 additions & 0 deletions src/Laravel/Console/Maker/Resources/skeleton/StateProvider.tpl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace {{ namespace }};

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;

final class {{ class_name }} implements ProviderInterface
{
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
// Retrieve the state from somewhere
}
}
85 changes: 85 additions & 0 deletions src/Laravel/Console/Maker/Utils/AppServiceProviderTagger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[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 ApiPlatform\Laravel\Console\Maker\Utils;

use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Filesystem\Filesystem;

final readonly class AppServiceProviderTagger
{
/** @var string */
private const APP_SERVICE_PROVIDER_PATH = 'Providers/AppServiceProvider.php';

/** @var string */
private const ITEM_PROVIDER_USE_STATEMENT = 'use ApiPlatform\State\ProviderInterface;';

/** @var string */
private const ITEM_PROCESSOR_USE_STATEMENT = 'use ApiPlatform\State\ProcessorInterface;';

public function __construct(private Filesystem $filesystem)
{
}

/**
* @throws FileNotFoundException
*/
public function addTagToServiceProvider(string $providerName, StateTypeEnum $stateTypeEnum): void
{
$appServiceProviderPath = app_path(self::APP_SERVICE_PROVIDER_PATH);
if (!$this->filesystem->exists($appServiceProviderPath)) {
throw new \RuntimeException('The AppServiceProvider is missing!');
}

$serviceProviderContent = $this->filesystem->get($appServiceProviderPath);

$this->addUseStatement($serviceProviderContent, $this->getStateTypeStatement($stateTypeEnum));
$this->addUseStatement($serviceProviderContent, \sprintf('use App\\State\\%s;', $providerName));
$this->addTag($serviceProviderContent, $providerName, $appServiceProviderPath, $stateTypeEnum);
}

private function addUseStatement(string &$content, string $useStatement): void
{
if (!str_contains($content, $useStatement)) {
$content = preg_replace(
'/^(namespace\s[^;]+;\s*)(\n)/m',
"$1\n$useStatement$2",
$content,
1
);
}
}

private function addTag(string &$content, string $stateName, string $serviceProviderPath, StateTypeEnum $stateTypeEnum): void
{
$tagStatement = \sprintf("\n\n\t\t\$this->app->tag(%s::class, %sInterface::class);", $stateName, $stateTypeEnum->name);

if (!str_contains($content, $tagStatement)) {
$content = preg_replace(
'/(public function register\(\)[^{]*{)(.*?)(\s*}\s*})/s',
"$1$2$tagStatement$3",
$content
);

$this->filesystem->put($serviceProviderPath, $content);
}
}

private function getStateTypeStatement(StateTypeEnum $stateTypeEnum): string
{
return match ($stateTypeEnum) {
StateTypeEnum::Provider => self::ITEM_PROVIDER_USE_STATEMENT,
StateTypeEnum::Processor => self::ITEM_PROCESSOR_USE_STATEMENT,
};
}
}
60 changes: 60 additions & 0 deletions src/Laravel/Console/Maker/Utils/StateTemplateGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[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 ApiPlatform\Laravel\Console\Maker\Utils;

use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Filesystem\Filesystem;

final readonly class StateTemplateGenerator
{
public function __construct(private Filesystem $filesystem)
{
}

public function getFilePath(string $directoryPath, string $stateFileName): string
{
return $directoryPath.$stateFileName.'.php';
}

/**
* @throws FileNotFoundException
*/
public function generate(string $pathLink, string $stateClassName, StateTypeEnum $stateTypeEnum): void
{
$namespace = 'App\\State';
$template = $this->loadTemplate($stateTypeEnum);

$content = strtr($template, [
'{{ namespace }}' => $namespace,
'{{ class_name }}' => $stateClassName,
]);

$this->filesystem->put($pathLink, $content);
}

/**
* @throws FileNotFoundException
*/
private function loadTemplate(StateTypeEnum $stateTypeEnum): string
{
$templateFile = match ($stateTypeEnum) {
StateTypeEnum::Provider => 'StateProvider.tpl.php',
StateTypeEnum::Processor => 'StateProcessor.tpl.php',
};

$templatePath = \dirname(__DIR__).'/Resources/skeleton/'.$templateFile;

return $this->filesystem->get($templatePath);
}
}
20 changes: 20 additions & 0 deletions src/Laravel/Console/Maker/Utils/StateTypeEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[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 ApiPlatform\Laravel\Console\Maker\Utils;

enum StateTypeEnum
{
case Provider;
case Processor;
}
Loading