Skip to content

Commit

Permalink
implement make repository command
Browse files Browse the repository at this point in the history
  • Loading branch information
salehhashemi1992 committed Mar 8, 2024
1 parent f67e5f0 commit 59265c7
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 3 deletions.
12 changes: 12 additions & 0 deletions config/repository.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
<?php

return [
/*
|--------------------------------------------------------------------------
| Repository Paths
|--------------------------------------------------------------------------
|
| This option defines the path where your repositories are located. This allows the
| application to dynamically resolve repository paths based on your configuration,
| making it easy to organize and manage your application's data access layer.
|
*/
'path' => app_path('Repositories'), // Default repository path

/*
|--------------------------------------------------------------------------
| Default Pagination Settings
Expand Down
92 changes: 92 additions & 0 deletions src/Commands/MakeRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

declare(strict_types=1);

namespace Salehhashemi\Repository\Commands;

use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
use RuntimeException;

class MakeRepository extends Command
{
protected $signature = 'make:repository {name : The name of the model}';

protected $description = 'Creates a new repository, interface, and filter for the specified model';

public function handle(Filesystem $filesystem): void
{
$name = Str::studly($this->argument('name'));
$modelName = class_basename($name);
$modelNamespace = 'App\\Models'; // Assuming models are under the 'App\Models' namespace

// Derive paths and namespaces from configuration
$repositoryPath = config('repository.path', app_path('Repositories'));
$repositoryNamespace = $this->pathToNamespace($repositoryPath);

// Check and create the necessary directories
$this->ensureDirectoryExists($repositoryPath, $filesystem);

// Paths for generated files
$repositoryFilePath = "{$repositoryPath}/{$modelName}Repository.php";
$interfaceFilePath = "{$repositoryPath}/Contracts/{$modelName}RepositoryInterface.php";
$filterFilePath = "{$repositoryPath}/Filters/{$modelName}Filter.php";

// Generate repository, interface, and filter
$this->createFileFromStub('repository.stub', $repositoryFilePath, [
'{{namespace}}' => $repositoryNamespace,
'{{modelName}}' => $modelName,
'{{modelNamespace}}' => $modelNamespace,
'{{name}}' => "{$modelName}",
], $filesystem);

$this->createFileFromStub('repository-interface.stub', $interfaceFilePath, [
'{{namespace}}' => "{$repositoryNamespace}\\Contracts",
'{{modelName}}' => $modelName,
'{{modelNamespace}}' => $modelNamespace,
'{{name}}' => "{$modelName}",
], $filesystem);

$this->createFileFromStub('filter.stub', $filterFilePath, [
'{{namespace}}' => "{$repositoryNamespace}\\Filters",
'{{modelName}}' => $modelName,
'{{modelNamespace}}' => $modelNamespace,
'{{name}}' => "{$modelName}",
], $filesystem);

$this->info("Repository, interface, and filter for {$modelName} created successfully.");
}

protected function createFileFromStub(
string $stubName,
string $filePath,
array $replacements,
Filesystem $filesystem
): void {
$stubPath = __DIR__."/stubs/{$stubName}";
$content = $filesystem->get($stubPath);
$content = str_replace(array_keys($replacements), array_values($replacements), $content);

if (! $filesystem->put($filePath, $content)) {
throw new RuntimeException("Failed to create file at {$filePath}");
}

$this->info("Created: {$filePath}");
}

protected function ensureDirectoryExists(string $path, Filesystem $filesystem): void
{
if (! $filesystem->isDirectory($path)) {
$filesystem->makeDirectory($path, 0755, true);
}
}

protected function pathToNamespace(string $path): string
{
// Convert file path to namespace
$namespace = str_replace([app_path(), '/', '\\'], ['', '\\', '\\'], $path);

return 'App'.($namespace ? "\\{$namespace}" : '');
}
}
39 changes: 39 additions & 0 deletions src/Commands/stubs/filter.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);

namespace {{namespace}}\Filters;

use Salehhashemi\Repository\BaseFilter;
use Illuminate\Database\Eloquent\Builder;
use {{modelNamespace}}\{{modelName}};

class {{name}}Filter extends BaseFilter
{
/**
* Apply filters to the query builder based on a set of parameters.
*/
public function applyFilters(array $filters): Builder
{
$this
->whereLike('title', $queryParams['title'] ?? '', self::WILD_BOTH)
->whereValue('status', $queryParams['status'] ?? '')
->compare('created_at', '>=', $queryParams['created_from'] ?? '')
->compare('created_at', '<=', $queryParams['created_to'] ?? '');

if (! empty($queryParams['category_id'])) {
$this->getQuery()->whereHas('categories', function ($query) use ($queryParams) {
$query->where('categories.id', $queryParams['category_id']);
});
}

return $this->getQuery();
}

/**
* The Eloquent model class that this filter applies to.
*/
protected function getModelClass(): string
{
return {{modelName}}::class;
}
}
18 changes: 18 additions & 0 deletions src/Commands/stubs/repository-interface.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);

namespace {{namespace}}\Contracts;

use Illuminate\Contracts\Pagination\Paginator;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Salehhashemi\Repository\Contracts\RepositoryInterface;
use Salehhashemi\Repository\Contracts\SearchableRepositoryInterface;

/**
* @method \{{modelNamespace}}\{{modelName}}|null findOne(int|string $primaryKey = null)
* @method \{{modelNamespace}}\{{modelName}} findOneOrFail(int|string $primaryKey = null)
*/
interface {{name}}RepositoryInterface extends RepositoryInterface, SearchableRepositoryInterface
{
// ...
}
34 changes: 34 additions & 0 deletions src/Commands/stubs/repository.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);

namespace {{namespace}};

use {{namespace}}\Contracts\{{name}}RepositoryInterface;
use Salehhashemi\Repository\Eloquent\BaseRepository;
use {{modelNamespace}}\{{modelName}};
use {{namespace}}\Filters\{{name}}Filter;
use Illuminate\Database\Eloquent\Builder;

class {{name}}Repository extends BaseRepository implements {{name}}RepositoryInterface
{
/**
* Get the filter manager for this repository.
*/
protected function getFilterManager(): {{name}}Filter
{
$filterManager = new {{name}}Filter();
$filterManager->setQuery($this->getQuery());

return $filterManager;
}

/**
* Specify Model class name
*/
public function model(): string
{
return {{modelName}}::class;
}

// Add other method implementations as needed
}
6 changes: 3 additions & 3 deletions src/Contracts/RepositoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
interface RepositoryInterface
{
/**
* It applies the conditions, criteria, relations, and order by to the query,
* It applies the conditions, criteria, relations, and orders by to the query,
* then gets the first result and resets the query
*/
public function findOne(int|string|null $primaryKey = null): ?Model;
Expand All @@ -32,8 +32,8 @@ public function findAll(array $options = []): EloquentCollection;
/**
* It returns a collection of key-value pairs from the database.
*
* @param string|null $key The key to use for the list.
* @param string|null $value The field to use as the value of the select list.
* @param string|null $key The key to use for the list.
* @param string|null $value The field to use as the value of the select list.
* @return \Illuminate\Support\Collection A collection of key value pairs.
*/
public function findList(?string $key = null, ?string $value = null): Collection;
Expand Down
7 changes: 7 additions & 0 deletions src/RepositoryServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Salehhashemi\Repository;

use Illuminate\Support\ServiceProvider;
use Salehhashemi\Repository\Commands\MakeRepository;

class RepositoryServiceProvider extends ServiceProvider
{
Expand All @@ -15,6 +16,12 @@ public function boot(): void
$this->publishes([
__DIR__.'/../config/repository.php' => config_path('repository.php'),
], 'config');

if ($this->app->runningInConsole()) {
$this->commands([
MakeRepository::class,
]);
}
}

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

namespace Salehhashemi\Repository\Tests;

use Illuminate\Support\Facades\File;

class MakeRepositoryCommandTest extends BaseTest
{
protected string $model = 'TestModel';

protected string $basePath;

protected function setUp(): void
{
parent::setUp();

$this->basePath = config('repository.path', app_path('Repositories'));

// Ensure the target directory exists
if (! File::isDirectory($this->basePath)) {
File::makeDirectory($this->basePath, 0755, true);
}
}

protected function tearDown(): void
{
// Cleanup: Remove the directories and files created during the test
File::deleteDirectory($this->basePath);

parent::tearDown();
}

public function testRepositoryStubGeneration(): void
{
$this->artisan('make:repository', ['name' => $this->model]);

$expectedFiles = [
"{$this->basePath}/{$this->model}Repository.php",
"{$this->basePath}/Contracts/{$this->model}RepositoryInterface.php",
"{$this->basePath}/Filters/{$this->model}Filter.php",
];

foreach ($expectedFiles as $file) {
$this->assertTrue(File::exists($file), "$file does not exist.");
}
}
}

0 comments on commit 59265c7

Please sign in to comment.