Skip to content

Commit

Permalink
Merge pull request #2 from moufmouf/master
Browse files Browse the repository at this point in the history
Refactoring naming strategy to account for getter/setter namings too.
  • Loading branch information
moufmouf committed May 17, 2017
2 parents 14dd451 + dd7d8b5 commit a594b81
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 77 deletions.
11 changes: 10 additions & 1 deletion doc/configuring_naming.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ When configuring the naming, you have 2 solutions:
- configure the default `DefaultNamingStrategy` instance
- provide your own `NamingStrategyInterface` implementation to TDBM

<div class="alert alert-info"><b>Note:</b> The naming strategy also covers:
<ul>
<li>Name of getters</li>
<li>Name of setters</li>
<li>Name of find-by-index methods</li>
<li>Name of JSON serialized properties</li>
</ul>
</div>

Configuring the default naming strategy
---------------------------------------

Expand Down Expand Up @@ -60,6 +69,6 @@ $strategy->setExceptions([
Implementing your own naming strategy
-------------------------------------

If you need a more fine-grained control over the naming strategy, you can simply implement your own `NamingStrategyInterface` class.
If you need a more fine-grained control over the naming strategy, you can simply implement your own `NamingStrategyInterface` class. Or you can extend the `AbstractNamingStrategy` class that implements most of the boilerplate code you will need and still offers a large degree of freedom.

The naming strategy is passed as a parameter of the `Configuration` class used to configure the `TDBMService`.
29 changes: 18 additions & 11 deletions src/Utils/AbstractBeanPropertyDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ abstract class AbstractBeanPropertyDescriptor
* @var bool
*/
protected $alternativeName = false;
/**
* @var NamingStrategyInterface
*/
protected $namingStrategy;

/**
* @param Table $table
* @param NamingStrategyInterface $namingStrategy
*/
public function __construct(Table $table)
public function __construct(Table $table, NamingStrategyInterface $namingStrategy)
{
$this->table = $table;
$this->namingStrategy = $namingStrategy;
}

/**
Expand All @@ -53,24 +59,17 @@ abstract public function getParamAnnotation();

public function getVariableName()
{
return '$'.$this->getLowerCamelCaseName();
}

public function getLowerCamelCaseName()
{
return TDBMDaoGenerator::toVariableName($this->getUpperCamelCaseName());
return $this->namingStrategy->getVariableName($this);
}

abstract public function getUpperCamelCaseName();

public function getSetterName()
{
return 'set'.$this->getUpperCamelCaseName();
return $this->namingStrategy->getSetterName($this);
}

public function getGetterName()
{
return 'get'.$this->getUpperCamelCaseName();
return $this->namingStrategy->getGetterName($this);
}

/**
Expand Down Expand Up @@ -136,4 +135,12 @@ abstract public function getGetterSetterCode();
* @return string
*/
abstract public function getJsonSerializeCode();

/**
* @return bool
*/
public function isAlternativeName(): bool
{
return $this->alternativeName;
}
}
81 changes: 81 additions & 0 deletions src/Utils/AbstractNamingStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php


namespace TheCodingMachine\TDBM\Utils;

use Doctrine\DBAL\Schema\Index;

/**
* An abstract class offering a common implementation for most names of the NamingStrategyInterface.
* It assumes getters and setters will start with get... and set..., and so on.
*/
abstract class AbstractNamingStrategy implements NamingStrategyInterface
{
protected abstract function getUpperCamelCaseName(AbstractBeanPropertyDescriptor $property): string;

protected function getLowerCamelCaseName(AbstractBeanPropertyDescriptor $property): string
{
return lcfirst($this->getUpperCamelCaseName($property));
}

/**
* Returns the getter name generated for the property passed in parameter.
*
* @param AbstractBeanPropertyDescriptor $property
* @return string
*/
public function getGetterName(AbstractBeanPropertyDescriptor $property): string
{
return 'get'.$this->getUpperCamelCaseName($property);
}

/**
* Returns the setter name generated for the property passed in parameter.
*
* @param AbstractBeanPropertyDescriptor $property
* @return string
*/
public function getSetterName(AbstractBeanPropertyDescriptor $property): string
{
return 'set'.$this->getUpperCamelCaseName($property);
}

/**
* Returns the variable name used in the setter generated for the property passed in parameter.
*
* @param AbstractBeanPropertyDescriptor $property
* @return string
*/
public function getVariableName(AbstractBeanPropertyDescriptor $property): string
{
return '$'.$this->getLowerCamelCaseName($property);
}

/**
* Returns the label of the JSON property for the property passed in parameter.
*
* @param AbstractBeanPropertyDescriptor $property
* @return string
*/
public function getJsonProperty(AbstractBeanPropertyDescriptor $property): string
{
return $this->getLowerCamelCaseName($property);
}

/**
* Returns the name of the find method attached to an index.
*
* @param AbstractBeanPropertyDescriptor[] $elements The list of properties in the index.
* @return string
*/
public function getFindByIndexMethodName(Index $index, array $elements): string
{
$methodNameComponent = array_map([$this, 'getUpperCamelCaseName'], $elements);

if ($index->isUnique()) {
return 'findOneBy'.implode('And', $methodNameComponent);
} else {
return 'findBy' . implode('And', $methodNameComponent);
}
}
}
12 changes: 5 additions & 7 deletions src/Utils/BeanDescriptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ private function getPropertiesForTable(Table $table)
/** @var $names AbstractBeanPropertyDescriptor[] */
$names = [];
foreach ($beanPropertyDescriptors as $beanDescriptor) {
$name = $beanDescriptor->getUpperCamelCaseName();
$name = $beanDescriptor->getGetterName();
if (isset($names[$name])) {
$names[$name]->useAlternativeName();
$beanDescriptor->useAlternativeName();
Expand All @@ -229,7 +229,7 @@ private function getPropertiesForTable(Table $table)
// Final check (throw exceptions if problem arises)
$names = [];
foreach ($beanPropertyDescriptors as $beanDescriptor) {
$name = $beanDescriptor->getUpperCamelCaseName();
$name = $beanDescriptor->getGetterName();
if (isset($names[$name])) {
throw new TDBMException('Unsolvable name conflict while generating method name');
} else {
Expand All @@ -240,7 +240,7 @@ private function getPropertiesForTable(Table $table)
// Last step, let's rebuild the list with a map:
$beanPropertyDescriptorsMap = [];
foreach ($beanPropertyDescriptors as $beanDescriptor) {
$beanPropertyDescriptorsMap[$beanDescriptor->getLowerCamelCaseName()] = $beanDescriptor;
$beanPropertyDescriptorsMap[$beanDescriptor->getVariableName()] = $beanDescriptor;
}

return $beanPropertyDescriptorsMap;
Expand Down Expand Up @@ -545,11 +545,9 @@ private function generateFindByDaoCodeForIndex(Index $index, $beanNamespace, $be
return [[], ''];
}

$methodNameComponent = [];
$functionParameters = [];
$first = true;
foreach ($elements as $element) {
$methodNameComponent[] = $element->getUpperCamelCaseName();
$functionParameter = $element->getClassName();
if ($functionParameter) {
$usedBeans[] = $beanNamespace.'\\'.$functionParameter;
Expand Down Expand Up @@ -583,8 +581,9 @@ private function generateFindByDaoCodeForIndex(Index $index, $beanNamespace, $be
}
$paramsString = implode("\n", $params);

$methodName = $this->namingStrategy->getFindByIndexMethodName($index, $elements);

if ($index->isUnique()) {
$methodName = 'findOneBy'.implode('And', $methodNameComponent);
$returnType = "{$beanClassName}";

$code = "
Expand All @@ -603,7 +602,6 @@ public function $methodName($functionParametersString, array \$additionalTablesF
}
";
} else {
$methodName = 'findBy'.implode('And', $methodNameComponent);
$returnType = "{$beanClassName}[]|ResultIterator|ResultArray";

$code = "
Expand Down
77 changes: 74 additions & 3 deletions src/Utils/DefaultNamingStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
namespace TheCodingMachine\TDBM\Utils;

use Doctrine\Common\Inflector\Inflector;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use TheCodingMachine\TDBM\TDBMException;

class DefaultNamingStrategy implements NamingStrategyInterface
class DefaultNamingStrategy extends AbstractNamingStrategy
{
private $beanPrefix = '';
private $beanSuffix = '';
Expand Down Expand Up @@ -158,7 +161,6 @@ private function toSingularCamelCase(string $str): string
}

$tokens = preg_split("/[_ ]+/", $str);
$tokens = array_map([Inflector::class, 'singularize'], $tokens);

$str = '';
foreach ($tokens as $token) {
Expand All @@ -168,6 +170,30 @@ private function toSingularCamelCase(string $str): string
return $str;
}

/**
* Put string to camel case form.
*
* @param $str string
*
* @return string
*/
private function toCamelCase(string $str): string
{
// Let's first check if this is not in the exceptions directory.
if (isset($this->exceptions[$str])) {
return $this->exceptions[$str];
}

$tokens = preg_split("/[_ ]+/", $str);

$str = '';
foreach ($tokens as $token) {
$str .= ucfirst($token);
}

return $str;
}

/**
* Returns the class name for the DAO factory.
*
Expand All @@ -192,8 +218,53 @@ public function getDaoFactoryClassName(): string
*
* @param array<string,string> $exceptions
*/
public function setExceptions(array $exceptions)
public function setExceptions(array $exceptions): void
{
$this->exceptions = $exceptions;
}

protected function getForeignKeyUpperCamelCaseName(ForeignKeyConstraint $foreignKey, bool $alternativeName): string
{
// First, are there many column or only one?
// If one column, we name the setter after it. Otherwise, we name the setter after the table name
if (count($foreignKey->getLocalColumns()) > 1) {
$name = $this->toSingularCamelCase($foreignKey->getForeignTableName());
if ($alternativeName) {
$camelizedColumns = array_map(['TheCodingMachine\\TDBM\\Utils\\TDBMDaoGenerator', 'toCamelCase'], $foreignKey->getLocalColumns());

$name .= 'By'.implode('And', $camelizedColumns);
}
} else {
$column = $foreignKey->getLocalColumns()[0];
// Let's remove any _id or id_.
if (strpos(strtolower($column), 'id_') === 0) {
$column = substr($column, 3);
}
if (strrpos(strtolower($column), '_id') === strlen($column) - 3) {
$column = substr($column, 0, strlen($column) - 3);
}
$name = $this->toCamelCase($column);
if ($alternativeName) {
$name .= 'Object';
}
}

return $name;
}

protected function getScalarColumnUpperCamelCaseName(string $columnName, bool $alternativeName): string
{
return $this->toCamelCase($columnName);
}

protected function getUpperCamelCaseName(AbstractBeanPropertyDescriptor $property): string
{
if ($property instanceof ObjectBeanPropertyDescriptor) {
return $this->getForeignKeyUpperCamelCaseName($property->getForeignKey(), $property->isAlternativeName());
} elseif ($property instanceof ScalarBeanPropertyDescriptor) {
return $this->getScalarColumnUpperCamelCaseName($property->getColumnName(), $property->isAlternativeName());
} else {
throw new TDBMException('Unexpected property type. Should be either ObjectBeanPropertyDescriptor or ScalarBeanPropertyDescriptor'); // @codeCoverageIgnore
}
}
}
44 changes: 44 additions & 0 deletions src/Utils/NamingStrategyInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@


namespace TheCodingMachine\TDBM\Utils;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index;

/**
* Generates bean / dao / property / method names from the database model.
Expand Down Expand Up @@ -46,4 +49,45 @@ public function getBaseDaoClassName(string $tableName) : string;
* @return string
*/
public function getDaoFactoryClassName() : string;

/**
* Returns the getter name generated for the property passed in parameter.
*
* @param AbstractBeanPropertyDescriptor $property
* @return string
*/
public function getGetterName(AbstractBeanPropertyDescriptor $property): string;

/**
* Returns the setter name generated for the property passed in parameter.
*
* @param AbstractBeanPropertyDescriptor $property
* @return string
*/
public function getSetterName(AbstractBeanPropertyDescriptor $property): string;

/**
* Returns the variable name used in the setter generated for the property passed in parameter.
*
* @param AbstractBeanPropertyDescriptor $property
* @return string
*/
public function getVariableName(AbstractBeanPropertyDescriptor $property): string;

/**
* Returns the label of the JSON property for the property passed in parameter.
*
* @param AbstractBeanPropertyDescriptor $property
* @return string
*/
public function getJsonProperty(AbstractBeanPropertyDescriptor $property): string;

/**
* Returns the name of the find method attached to an index.
*
* @param Index $index
* @param AbstractBeanPropertyDescriptor[] $elements The list of properties in the index.
* @return string
*/
public function getFindByIndexMethodName(Index $index, array $elements): string;
}

0 comments on commit a594b81

Please sign in to comment.