Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor bundle + add test #5

Merged
merged 1 commit into from
Aug 25, 2023
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
1 change: 1 addition & 0 deletions .config/composer/.htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deny from all
33 changes: 33 additions & 0 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#.github/workflows/php.yml
name: QueryBuilderRepositoryGeneratorBundle test

on:
push: ~
pull_request: ~

jobs:
build:
runs-on: ${{ matrix.operating-system }}
name: PHP ${{ matrix.php }} and Symfony ${{ matrix.symfony }}
strategy:
matrix:
operating-system: [ ubuntu-latest ]
php: [ '8.1' ]
symfony: ['5.4.*', '6.0.*', '6.1.*', '6.2.*', '6.3.*']

steps:
- uses: actions/checkout@main

- name: Setup PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: flex

- name: Download dependencies
env:
SYMFONY_REQUIRE: ${{ matrix.symfony }}
uses: ramsey/composer-install@v1

- name: Run test suite on PHP ${{ matrix.php }} and Symfony ${{ matrix.symfony }}
run: ./vendor/bin/phpunit
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/vendor
/.phpunit.result.cache
/.phpunit.cache/test-results
/tests/visualizer-default.yml
/composer.lock
/tests/src/Repository/MyClassRepositoryBase.php
11 changes: 5 additions & 6 deletions Configuration/Configurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
class Configurator
{
const DEFAULT_REPOSITORY_EXTEND = '\\Doctrine\\Bundle\\DoctrineBundle\\Repository\\ServiceEntityRepository';
protected $entityConfigurations = [];

public function __construct($entityConfigurations, $repositoryExtensions)
{
$this->entityConfigurations = $entityConfigurations;
$this->repositoryExtensions = $repositoryExtensions;
public function __construct(
private array $entityConfigurations = [],
private array $repositoryExtensions = [],
) {
}

public function getEntityDqlName($entityName): string
public function getEntityDqlName(string $entityName): string
{
if (isset($this->entityConfigurations[$entityName])) {
$entityDqlName = $this->entityConfigurations[$entityName]['querybuilder_name'];
Expand Down
33 changes: 33 additions & 0 deletions Generator/Persister.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace tbn\QueryBuilderRepositoryGeneratorBundle\Generator;

use Symfony\Component\Filesystem\Filesystem;

class Persister
{
public function persistClass($filePath, $content)
{
$directory = $this->getDirectoryFromFilepath($filePath);

//create if needed the repertory
$this->createRepertory($directory);

// Stores the cache
file_put_contents($filePath, $content);
}

private function createRepertory(string $path): void
{
$fs = new Filesystem();
$fs->mkdir($path);
}

private function getDirectoryFromFilepath(string $filePath): string
{
$pathParts = explode('/', $filePath);
array_pop($pathParts);

return implode('/', $pathParts);
}
}
216 changes: 57 additions & 159 deletions Generator/RepositoryGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,209 +2,107 @@

namespace tbn\QueryBuilderRepositoryGeneratorBundle\Generator;

use Doctrine\Bundle\DoctrineBundle\Mapping\DisconnectedMetadataFactory;
use Doctrine\ORM\Mapping\ClassMetadata;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper;
use Symfony\Component\Filesystem\Filesystem;
use tbn\QueryBuilderRepositoryGeneratorBundle\Configuration\Configurator;
use Twig\Environment;

class RepositoryGenerator
{
protected $configurator = null;
private $doctrineHelper;

/**
* @var Environment
*/
private $twig;

public function __construct(
$topRepositoryTemple,
$columnTemplate,
$bottomRepositoryTemplate,
$bundles,
DoctrineHelper $doctrineHelper,
$kernel,
Configurator $configurator,
$associationTemplate,
$twig
private TemplateService $templateService,
private $bundles,
private DoctrineHelper $doctrineHelper,
private Configurator $configurator,
private Persister $persister,
private LoggerInterface $logger,
) {
$this->doctrineHelper = $doctrineHelper;
$this->kernel = $kernel;
//the bundles to scan
$this->bundles = $bundles;

//the templates
$this->topRepositoryTemple = $topRepositoryTemple;
$this->columnTemplate = $columnTemplate;
$this->bottomRepositoryTemplate = $bottomRepositoryTemplate;
$this->associationTemplate = $associationTemplate;

$this->configurator = $configurator;
$this->twig = $twig;
}

/**
* Generate all files
*
*/
public function generateFiles()
{
//the bundles to scan
$bundles = $this->bundles;
$configurator = $this->configurator;

//services
$twig = $this->twig;

//parse the bundles
foreach ($bundles as $bundleName) {
$this->logger->info('Bundle analysed: '. $bundleName);

//the path of the files
$allMetadata = $this->doctrineHelper->getMetadata($bundleName);

/** @var ClassMetadata $meta */
foreach ($allMetadata as $meta) {
$customRepositoryClassName = $meta->customRepositoryClassName;
if ($customRepositoryClassName) {
$entityClasspath = $meta->name;
$fieldMappings = $meta->fieldMappings;
$associationMappings = $meta->associationMappings;
$customRepositoryClassName = $meta->customRepositoryClassName;

$pathParts = explode('\\', $customRepositoryClassName);
$entityClassname = end($pathParts);

$entityDql = $configurator->getEntityDqlName($meta->name);

$entityNamespace = $this->getNamespaceFromFilepath($customRepositoryClassName);

$idType = null;
if (isset($meta->fieldMappings['id'])) {
$idField = $meta->fieldMappings['id'];
$idType = $idField['type'];
}
$renderedTemplate = $this->renderTopClass($entityNamespace, $entityClasspath, $entityClassname, $bundleName, $entityDql, $idType);

//parse the columns
foreach ($fieldMappings as $fieldMapping) {
$renderedTemplate .= $this->renderField($fieldMapping, $entityDql);
}

foreach ($associationMappings as $associationMapping) {
$targetEntityMetadata = $allMetadata[$associationMapping['targetEntity']];
$renderedTemplate .= $this->renderAssociation($associationMapping, $entityDql, $targetEntityMetadata);
}

//get the bottom template
$renderedTemplate .= $twig->render($this->bottomRepositoryTemplate);

//store the generated content
$reflector = new \ReflectionClass($customRepositoryClassName);
$originalRepostoryPath = $reflector->getFileName();
$fullPath = str_replace('.php', 'Base.php', $originalRepostoryPath);

if (!empty($customRepositoryClassName)) {
$this->persistClass($fullPath, $renderedTemplate);
}
if ($meta->customRepositoryClassName === null) {
$this->logger->info('SKIPPING entity: '. $meta->name);
continue;
}
}
}
}

protected function createRepertory($path)
{
$fs = new Filesystem();
$fs->mkdir($path);
}
$this->logger->info('Entity: '. $meta->name);
$renderedTemplate = $this->generateRepository($allMetadata, $bundleName, $meta);

protected function persistClass($filePath, $content)
{
$directory = $this->getDirectoryFromFilepath($filePath);
//store the generated content
$reflector = new \ReflectionClass($customRepositoryClassName);
$originalRepostoryPath = $reflector->getFileName();
$fullPath = str_replace('.php', 'Base.php', $originalRepostoryPath);

//create if needed the repertory
$this->createRepertory($directory);
$this->putFileContent($filePath, $content);
$this->persister->persistClass($fullPath, $renderedTemplate);
}
}
}

protected function getDirectoryFromFilepath($filePath): string
private function generateRepository(array $allMetadata, string $bundleName, ClassMetadata $meta)
{
$pathParts = explode('/', $filePath);
array_pop($pathParts);
$entityClasspath = $meta->name;
$fieldMappings = $meta->fieldMappings;
$associationMappings = $meta->associationMappings;
$customRepositoryClassName = $meta->customRepositoryClassName;

return implode('/', $pathParts);
}
$pathParts = explode('\\', $customRepositoryClassName);
$entityClassname = end($pathParts);

protected function getNamespaceFromFilepath($filePath): string
{
$pathParts = explode('\\', $filePath);
array_pop($pathParts);
$entityDql = $this->configurator->getEntityDqlName($meta->name);

return implode('\\', $pathParts);
}

protected function renderTopClass($namespace, $entityClasspath, $entityClassname, $bundleName, $entityDql, $idType): string
{
//services
$twig = $this->twig;
$entityNamespace = $this->getNamespaceFromFilepath($customRepositoryClassName);

$idType = null;
if (isset($meta->fieldMappings['id'])) {
$idField = $meta->fieldMappings['id'];
$idType = $idField['type'];
}
$extendClass = $this->configurator->getExtendRepository($entityClasspath);

$topClassparameter = array(
'namespace' => $namespace,
'entityClasspath' => $entityClasspath,
'entityClassname' => $entityClassname,
'extendClass' => $extendClass,
'bundleName' => $bundleName,
'entityDql' => $entityDql,
'idType' => $idType,
$renderedTemplate = $this->templateService->renderTopClass(
$extendClass,
$entityNamespace,
$entityClasspath,
$entityClassname,
$bundleName,
$entityDql,
$idType,
);

return $twig->render($this->topRepositoryTemple, $topClassparameter);
}

protected function renderAssociation($associationMapping, $entityDql, $targetEntityMetadata): string
{
//services
$twig = $this->twig;

$idType = null;
//parse the columns
foreach ($fieldMappings as $fieldMapping) {
$renderedTemplate .= $this->templateService->renderField($fieldMapping, $entityDql);
}

if (isset($targetEntityMetadata->fieldMappings['id'])) {
$idField = $targetEntityMetadata->fieldMappings['id'];
$idType = $idField['type'];
foreach ($associationMappings as $associationMapping) {
$targetEntityMetadata = $allMetadata[$associationMapping['targetEntity']];
$renderedTemplate .= $this->templateService->renderAssociation($associationMapping, $entityDql, $targetEntityMetadata);
}

$fieldName = $associationMapping['fieldName'];
$parameters = array(
'entityDql' => $entityDql,
'column' => ucfirst($fieldName),
'columnDql' => $fieldName,
'idType' => $idType,
'targetEntity' => $associationMapping['targetEntity'],
);
//get the bottom template
$renderedTemplate .= $this->templateService->renderBottomClass();

return $twig->render($this->associationTemplate, $parameters);
return $renderedTemplate;
}

protected function renderField($fieldMapping, $entityDql): string
private function getNamespaceFromFilepath($filePath): string
{
//services
$twig = $this->twig;

$fieldName = $fieldMapping['fieldName'];
$parameters = array(
'entityDql' => $entityDql,
'column' => ucfirst($fieldName),
'columnDql' => $fieldName,
);

return $twig->render($this->columnTemplate, $parameters);
}
$pathParts = explode('\\', $filePath);
array_pop($pathParts);

protected function putFileContent($fileName, $content)
{
// Stores the cache
file_put_contents($fileName, $content);
return implode('\\', $pathParts);
}
}
Loading
Loading