Skip to content

Commit

Permalink
Refactor bundle + add test
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasbeaujean committed Aug 25, 2023
1 parent 7126b74 commit c5db052
Show file tree
Hide file tree
Showing 16 changed files with 797 additions and 174 deletions.
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

0 comments on commit c5db052

Please sign in to comment.