Skip to content

Commit

Permalink
refactor: 💥 Handled model option by context in Policies
Browse files Browse the repository at this point in the history
- When passing the `--model` options together with the `--context` option then the `use` statement in the policy must follow the context namespace, not the laravel pattern.
- When not using the `--context` option, only the `--model` option, the model must be created in the default Laravel folder

closes #5
  • Loading branch information
allysonsilva committed May 5, 2022
1 parent 793ce53 commit abfd06d
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 5 deletions.
17 changes: 12 additions & 5 deletions src/Commands/Foundation/Concerns/BuildClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,24 @@ abstract protected function getContextComponentFolderNamespace(): string;
* Get the context namespace for the class.
* Get the full namespace for a given class, without the class name.
*
* @param string $componentFolder
*
* @return string
*/
protected function getContextNamespace(): string
protected function getContextNamespace(string $componentFolder = ''): string
{
$rootNamespace = trim($this->rootNamespace(), '\\');

if (! empty($contextOption = $this->contextOption())) {
return $rootNamespace . '\\' .
config('context.folders.domain') . '\\' .
$contextOption . '\\' .
$this->getComponentFolderNamespace();
$context = $rootNamespace . '\\' .
config('context.folders.domain') . '\\' .
$contextOption . '\\';

if (! empty($componentFolder)) {
return $context . $componentFolder;
}

return $context . $this->getComponentFolderNamespace();
}

return parent::getDefaultNamespace($rootNamespace);
Expand Down
30 changes: 30 additions & 0 deletions src/Commands/Foundation/PolicyMakeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,34 @@ protected function getContextComponentFolderNamespace(): string
{
return config('context.folders.components.policies');
}

/**
* Qualify the given model class base name.
* Get the fully-qualified model class name.
*
* @param string $model
*
* @return string
*/
protected function qualifyModel(string $model): string
{
$model = ltrim($model, '\\/');
$model = str_replace('/', '\\', $model);

$rootNamespace = $this->rootNamespace();

if (str_starts_with($model, $rootNamespace)) {
return $model;
}

if (! empty($this->contextOption())) {
$modelsComponentFolder = strval(config('context.folders.components.models'));

return $this->getContextNamespace($modelsComponentFolder) . "\\{$model}";
}

return is_dir(app_path('Models'))
? $rootNamespace . 'Models\\' . $model
: $rootNamespace . $model;
}
}
117 changes: 117 additions & 0 deletions tests/Unit/Commands/Foundation/PolicyMakeCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace Allyson\ArtisanDomainContext\Tests\Unit\Commands\Foundation;

use PhpToken;
use Illuminate\Support\Facades\File;
use Allyson\ArtisanDomainContext\Tests\Unit\MakeCommandTestCase;

/**
* @group Making
*/
class PolicyMakeCommandTest extends MakeCommandTestCase
{
private string $commandName = 'make:policy';
private string $returnMessage = 'Policy created successfully.';

/**
* Class name for use in assertions.
*
* @return string
*/
protected function className(): string
{
return "Foo{$this->randomString()}Policy";
}

/**
* Model class name for use in assertions.
*
* @return string
*/
protected function modelClassName(): string
{
return 'YourModel';
}

/**
* Component folder name. Where are the component classes.
*
* @return string
*/
protected function componentFolder(): string
{
return config('context.folders.components.policies');
}

/**
* @test
* @testdox When using the `--model` option and also the `--context` options, then the model must be referenced to the context namespace
*/
public function creatingPolicyWithCustomModel()
{
$policyClassName = $this->className();

$modelsFolder = config('context.folders.components.models');
$modelsComponentNamespace = $this->getDomainComponentNamespace($modelsFolder, $this->contextFolder) . "\\{$this->modelClassName()}";

$this->artisan($this->commandName, [
'--context' => $this->contextFolder,
'--model' => $this->modelClassName(),
'name' => $policyClassName
])
->assertSuccessful()
->expectsOutput($this->returnMessage);

$policyFilepath = $this->getComponentFilepath($policyClassName, $this->componentFolder(), $this->contextFolder);
$hasModelInUseStatement = $this->hasModelInUseStatement($policyFilepath, $modelsComponentNamespace);

File::delete($policyFilepath);

self::assertTrue($hasModelInUseStatement);
}

/**
* @test
* @testdox Creating a policy in laravel's default folder with a custom model with the `--model` option
*/
public function creatingPolicyWithCustomModelInLaravelDefaultFolder()
{
$policyClassName = $this->className();
$modelsPath = app_path('Models');

File::makeDirectory(path: $modelsPath, force: true);

$this->artisan($this->commandName, [
'--model' => $this->modelClassName(),
'name' => $policyClassName
])
->assertSuccessful()
->expectsOutput($this->returnMessage);

$policyFilepath = app_path("Policies/{$policyClassName}.php");
$hasModelInUseStatement = $this->hasModelInUseStatement($policyFilepath, "App\\Models\\{$this->modelClassName()}");

File::delete($policyFilepath);
File::deleteDirectory($modelsPath);

self::assertTrue($hasModelInUseStatement);
}

/**
* Checks if the policy class has a use statement referring to the model.
*
* @param string $policyFilepath
* @param string $modelsComponentNamespace
*
* @return bool
*/
private function hasModelInUseStatement(string $policyFilepath, string $modelsComponentNamespace): bool
{
$policyTokens = PhpToken::tokenize(file_get_contents($policyFilepath));
$tokensFullQualified = array_filter($policyTokens, fn (PhpToken $token) => $token->getTokenName() === 'T_NAME_QUALIFIED');
$containsModelInUseStatement = ! empty(array_filter($tokensFullQualified, fn (PhpToken $token) => $token->text === $modelsComponentNamespace));

return $containsModelInUseStatement;
}
}

0 comments on commit abfd06d

Please sign in to comment.