Stole the TypeGuesser (and its tests) from symfony's propel bridge
This commit is contained in:
parent
b2abeb0ead
commit
44ec2b4b29
182
Form/PropelTypeGuesser.php
Normal file
182
Form/PropelTypeGuesser.php
Normal file
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Form;
|
||||
|
||||
use Propel\Runtime\Map\RelationMap;
|
||||
use Propel\Runtime\Util\PropelColumnTypes;
|
||||
use Symfony\Component\Form\FormTypeGuesserInterface;
|
||||
use Symfony\Component\Form\Guess\Guess;
|
||||
use Symfony\Component\Form\Guess\TypeGuess;
|
||||
use Symfony\Component\Form\Guess\ValueGuess;
|
||||
|
||||
/**
|
||||
* Propel Type guesser.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class PropelTypeGuesser implements FormTypeGuesserInterface
|
||||
{
|
||||
private $cache = array();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function guessType($class, $property)
|
||||
{
|
||||
if (!$table = $this->getTable($class)) {
|
||||
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
|
||||
}
|
||||
|
||||
foreach ($table->getRelations() as $relation) {
|
||||
if ($relation->getType() === RelationMap::MANY_TO_ONE) {
|
||||
if (strtolower($property) === strtolower($relation->getName())) {
|
||||
return new TypeGuess('model', array(
|
||||
'class' => $relation->getForeignTable()->getClassName(),
|
||||
'multiple' => false,
|
||||
), Guess::HIGH_CONFIDENCE);
|
||||
}
|
||||
} elseif ($relation->getType() === RelationMap::ONE_TO_MANY) {
|
||||
if (strtolower($property) === strtolower($relation->getPluralName())) {
|
||||
return new TypeGuess('model', array(
|
||||
'class' => $relation->getForeignTable()->getClassName(),
|
||||
'multiple' => true,
|
||||
), Guess::HIGH_CONFIDENCE);
|
||||
}
|
||||
} elseif ($relation->getType() === RelationMap::MANY_TO_MANY) {
|
||||
if (strtolower($property) == strtolower($relation->getPluralName())) {
|
||||
return new TypeGuess('model', array(
|
||||
'class' => $relation->getLocalTable()->getClassName(),
|
||||
'multiple' => true,
|
||||
), Guess::HIGH_CONFIDENCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$column = $this->getColumn($class, $property)) {
|
||||
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
|
||||
}
|
||||
|
||||
switch ($column->getType()) {
|
||||
case PropelColumnTypes::BOOLEAN:
|
||||
case PropelColumnTypes::BOOLEAN_EMU:
|
||||
return new TypeGuess('checkbox', array(), Guess::HIGH_CONFIDENCE);
|
||||
case PropelColumnTypes::TIMESTAMP:
|
||||
case PropelColumnTypes::BU_TIMESTAMP:
|
||||
return new TypeGuess('datetime', array(), Guess::HIGH_CONFIDENCE);
|
||||
case PropelColumnTypes::DATE:
|
||||
case PropelColumnTypes::BU_DATE:
|
||||
return new TypeGuess('date', array(), Guess::HIGH_CONFIDENCE);
|
||||
case PropelColumnTypes::TIME:
|
||||
return new TypeGuess('time', array(), Guess::HIGH_CONFIDENCE);
|
||||
case PropelColumnTypes::FLOAT:
|
||||
case PropelColumnTypes::REAL:
|
||||
case PropelColumnTypes::DOUBLE:
|
||||
case PropelColumnTypes::DECIMAL:
|
||||
return new TypeGuess('number', array(), Guess::MEDIUM_CONFIDENCE);
|
||||
case PropelColumnTypes::TINYINT:
|
||||
case PropelColumnTypes::SMALLINT:
|
||||
case PropelColumnTypes::INTEGER:
|
||||
case PropelColumnTypes::BIGINT:
|
||||
case PropelColumnTypes::NUMERIC:
|
||||
return new TypeGuess('integer', array(), Guess::MEDIUM_CONFIDENCE);
|
||||
case PropelColumnTypes::ENUM:
|
||||
case PropelColumnTypes::CHAR:
|
||||
if ($column->getValueSet()) {
|
||||
//check if this is mysql enum
|
||||
$choices = $column->getValueSet();
|
||||
$labels = array_map('ucfirst', $choices);
|
||||
|
||||
return new TypeGuess('choice', array('choices' => array_combine($choices, $labels)), Guess::MEDIUM_CONFIDENCE);
|
||||
}
|
||||
case PropelColumnTypes::VARCHAR:
|
||||
return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE);
|
||||
case PropelColumnTypes::LONGVARCHAR:
|
||||
case PropelColumnTypes::BLOB:
|
||||
case PropelColumnTypes::CLOB:
|
||||
case PropelColumnTypes::CLOB_EMU:
|
||||
return new TypeGuess('textarea', array(), Guess::MEDIUM_CONFIDENCE);
|
||||
default:
|
||||
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function guessRequired($class, $property)
|
||||
{
|
||||
if ($column = $this->getColumn($class, $property)) {
|
||||
return new ValueGuess($column->isNotNull(), Guess::HIGH_CONFIDENCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function guessMaxLength($class, $property)
|
||||
{
|
||||
if ($column = $this->getColumn($class, $property)) {
|
||||
if ($column->isTextType()) {
|
||||
return new ValueGuess($column->getSize(), Guess::HIGH_CONFIDENCE);
|
||||
}
|
||||
switch ($column->getType()) {
|
||||
case PropelColumnTypes::FLOAT:
|
||||
case PropelColumnTypes::REAL:
|
||||
case PropelColumnTypes::DOUBLE:
|
||||
case PropelColumnTypes::DECIMAL:
|
||||
return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function guessPattern($class, $property)
|
||||
{
|
||||
if ($column = $this->getColumn($class, $property)) {
|
||||
switch ($column->getType()) {
|
||||
case PropelColumnTypes::FLOAT:
|
||||
case PropelColumnTypes::REAL:
|
||||
case PropelColumnTypes::DOUBLE:
|
||||
case PropelColumnTypes::DECIMAL:
|
||||
return new ValueGuess(null, Guess::MEDIUM_CONFIDENCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getTable($class)
|
||||
{
|
||||
if (isset($this->cache[$class])) {
|
||||
return $this->cache[$class];
|
||||
}
|
||||
|
||||
if (class_exists($queryClass = $class.'Query')) {
|
||||
$query = new $queryClass();
|
||||
|
||||
return $this->cache[$class] = $query->getTableMap();
|
||||
}
|
||||
}
|
||||
|
||||
protected function getColumn($class, $property)
|
||||
{
|
||||
if (isset($this->cache[$class.'::'.$property])) {
|
||||
return $this->cache[$class.'::'.$property];
|
||||
}
|
||||
|
||||
$table = $this->getTable($class);
|
||||
|
||||
if ($table && $table->hasColumn($property)) {
|
||||
return $this->cache[$class.'::'.$property] = $table->getColumn($property);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
<parameter key="propel.data_collector.class">Propel\PropelBundle\DataCollector\PropelDataCollector</parameter>
|
||||
<parameter key="propel.logger.class">Propel\PropelBundle\Logger\PropelLogger</parameter>
|
||||
<parameter key="propel.twig.extension.syntax.class">Propel\PropelBundle\Twig\Extension\SyntaxExtension</parameter>
|
||||
<parameter key="form.type_guesser.propel.class">Propel\PropelBundle\Form\PropelTypeGuesser</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
|
@ -32,5 +33,9 @@
|
|||
<service id="propel.twig.extension.syntax" class="%propel.twig.extension.syntax.class%">
|
||||
<tag name="twig.extension" />
|
||||
</service>
|
||||
|
||||
<service id="form.type_guesser.propel" class="%form.type_guesser.propel.class%">
|
||||
<tag name="form.type_guesser" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
61
Tests/Fixtures/Column.php
Normal file
61
Tests/Fixtures/Column.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Runtime\Util\PropelColumnTypes;
|
||||
|
||||
class Column
|
||||
{
|
||||
private $name;
|
||||
|
||||
private $type;
|
||||
|
||||
public function __construct($name, $type)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function isTextType()
|
||||
{
|
||||
if (!$this->type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($this->type) {
|
||||
case PropelColumnTypes::CHAR:
|
||||
case PropelColumnTypes::VARCHAR:
|
||||
case PropelColumnTypes::LONGVARCHAR:
|
||||
case PropelColumnTypes::BLOB:
|
||||
case PropelColumnTypes::CLOB:
|
||||
case PropelColumnTypes::CLOB_EMU:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return $this->isTextType() ? 255 : 0;
|
||||
}
|
||||
|
||||
public function isNotNull()
|
||||
{
|
||||
return ('id' === $this->name);
|
||||
}
|
||||
}
|
109
Tests/Fixtures/Item.php
Normal file
109
Tests/Fixtures/Item.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Runtime\ActiveRecord\ActiveRecordInterface;
|
||||
use Propel\Runtime\Connection\ConnectionInterface;
|
||||
|
||||
class Item implements ActiveRecordInterface
|
||||
{
|
||||
private $id;
|
||||
|
||||
private $value;
|
||||
|
||||
private $groupName;
|
||||
|
||||
private $price;
|
||||
|
||||
public function __construct($id = null, $value = null, $groupName = null, $price = null)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->value = $value;
|
||||
$this->groupName = $groupName;
|
||||
$this->price = $price;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getGroupName()
|
||||
{
|
||||
return $this->groupName;
|
||||
}
|
||||
|
||||
public function getPrice()
|
||||
{
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
public function getPrimaryKey()
|
||||
{
|
||||
return $this->getId();
|
||||
}
|
||||
|
||||
public function setPrimaryKey($primaryKey)
|
||||
{
|
||||
$this->setId($primaryKey);
|
||||
}
|
||||
|
||||
public function isModified()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isColumnModified($col)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isNew()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setNew($b)
|
||||
{
|
||||
}
|
||||
|
||||
public function resetModified()
|
||||
{
|
||||
}
|
||||
|
||||
public function isDeleted()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setDeleted($b)
|
||||
{
|
||||
}
|
||||
|
||||
public function delete(ConnectionInterface $con = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function save(ConnectionInterface $con = null)
|
||||
{
|
||||
}
|
||||
}
|
100
Tests/Fixtures/ItemQuery.php
Normal file
100
Tests/Fixtures/ItemQuery.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Fixtures;
|
||||
|
||||
use Propel\Runtime\Map\ColumnMap;
|
||||
use Propel\Runtime\Map\RelationMap;
|
||||
use Propel\Runtime\Map\TableMap;
|
||||
use Propel\Runtime\Util\PropelColumnTypes;
|
||||
|
||||
class ItemQuery
|
||||
{
|
||||
private $map = array(
|
||||
'id' => PropelColumnTypes::INTEGER,
|
||||
'value' => PropelColumnTypes::VARCHAR,
|
||||
'price' => PropelColumnTypes::FLOAT,
|
||||
'is_active' => PropelColumnTypes::BOOLEAN,
|
||||
'enabled' => PropelColumnTypes::BOOLEAN_EMU,
|
||||
'updated_at' => PropelColumnTypes::TIMESTAMP,
|
||||
|
||||
'updated_at' => PropelColumnTypes::TIMESTAMP,
|
||||
'updated_at' => PropelColumnTypes::TIMESTAMP,
|
||||
'updated_at' => PropelColumnTypes::TIMESTAMP,
|
||||
);
|
||||
|
||||
public function getTableMap()
|
||||
{
|
||||
// Allows to define methods in this class
|
||||
// to avoid a lot of mock classes
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPrimaryKeys()
|
||||
{
|
||||
$cm = new ColumnMap('id', new TableMap());
|
||||
$cm->setType('INTEGER');
|
||||
|
||||
return array('id' => $cm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method from the TableMap API
|
||||
*/
|
||||
public function hasColumn($column)
|
||||
{
|
||||
return in_array($column, array_keys($this->map));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method from the TableMap API
|
||||
*/
|
||||
public function getColumn($column)
|
||||
{
|
||||
if ($this->hasColumn($column)) {
|
||||
return new Column($column, $this->map[$column]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method from the TableMap API
|
||||
*/
|
||||
public function getRelations()
|
||||
{
|
||||
// table maps
|
||||
$authorTable = new TableMap();
|
||||
$authorTable->setClassName('\Foo\Author');
|
||||
|
||||
$resellerTable = new TableMap();
|
||||
$resellerTable->setClassName('\Foo\Reseller');
|
||||
|
||||
// relations
|
||||
$mainAuthorRelation = new RelationMap('MainAuthor');
|
||||
$mainAuthorRelation->setType(RelationMap::MANY_TO_ONE);
|
||||
$mainAuthorRelation->setForeignTable($authorTable);
|
||||
|
||||
$authorRelation = new RelationMap('Author');
|
||||
$authorRelation->setType(RelationMap::ONE_TO_MANY);
|
||||
$authorRelation->setForeignTable($authorTable);
|
||||
|
||||
$resellerRelation = new RelationMap('Reseller');
|
||||
$resellerRelation->setType(RelationMap::MANY_TO_MANY);
|
||||
$resellerRelation->setLocalTable($resellerTable);
|
||||
|
||||
return array(
|
||||
$mainAuthorRelation,
|
||||
$authorRelation,
|
||||
$resellerRelation
|
||||
);
|
||||
}
|
||||
}
|
129
Tests/Form/PropelTypeGuesserTest.php
Normal file
129
Tests/Form/PropelTypeGuesserTest.php
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Propel\PropelBundle\Tests\Form;
|
||||
|
||||
use Propel\PropelBundle\Form\PropelTypeGuesser;
|
||||
use Propel\PropelBundle\Tests\TestCase;
|
||||
use Symfony\Component\Form\Guess\Guess;
|
||||
|
||||
class PropelTypeGuesserTest extends TestCase
|
||||
{
|
||||
const CLASS_NAME = 'Propel\PropelBundle\Tests\Fixtures\Item';
|
||||
|
||||
const UNKNOWN_CLASS_NAME = 'Propel\PropelBundle\Tests\Fixtures\UnknownItem';
|
||||
|
||||
private $guesser;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->guesser = new PropelTypeGuesser();
|
||||
}
|
||||
|
||||
public function testGuessMaxLengthWithText()
|
||||
{
|
||||
$value = $this->guesser->guessMaxLength(self::CLASS_NAME, 'value');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertEquals(255, $value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessMaxLengthWithFloat()
|
||||
{
|
||||
$value = $this->guesser->guessMaxLength(self::CLASS_NAME, 'price');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertNull($value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessMinLengthWithText()
|
||||
{
|
||||
$value = $this->guesser->guessPattern(self::CLASS_NAME, 'value');
|
||||
|
||||
$this->assertNull($value);
|
||||
}
|
||||
|
||||
public function testGuessMinLengthWithFloat()
|
||||
{
|
||||
$value = $this->guesser->guessPattern(self::CLASS_NAME, 'price');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertNull($value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessRequired()
|
||||
{
|
||||
$value = $this->guesser->guessRequired(self::CLASS_NAME, 'id');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertTrue($value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessRequiredWithNullableColumn()
|
||||
{
|
||||
$value = $this->guesser->guessRequired(self::CLASS_NAME, 'value');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertFalse($value->getValue());
|
||||
}
|
||||
|
||||
public function testGuessTypeWithoutTable()
|
||||
{
|
||||
$value = $this->guesser->guessType(self::UNKNOWN_CLASS_NAME, 'property');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertEquals('text', $value->getType());
|
||||
$this->assertEquals(Guess::LOW_CONFIDENCE, $value->getConfidence());
|
||||
}
|
||||
|
||||
public function testGuessTypeWithoutColumn()
|
||||
{
|
||||
$value = $this->guesser->guessType(self::CLASS_NAME, 'property');
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertEquals('text', $value->getType());
|
||||
$this->assertEquals(Guess::LOW_CONFIDENCE, $value->getConfidence());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderForGuessType
|
||||
*/
|
||||
public function testGuessType($property, $type, $confidence, $multiple = null)
|
||||
{
|
||||
$value = $this->guesser->guessType(self::CLASS_NAME, $property);
|
||||
|
||||
$this->assertNotNull($value);
|
||||
$this->assertEquals($type, $value->getType());
|
||||
$this->assertEquals($confidence, $value->getConfidence());
|
||||
|
||||
if ($type === 'model') {
|
||||
$options = $value->getOptions();
|
||||
|
||||
$this->assertSame($multiple, $options['multiple']);
|
||||
}
|
||||
}
|
||||
|
||||
public static function dataProviderForGuessType()
|
||||
{
|
||||
return array(
|
||||
array('is_active', 'checkbox', Guess::HIGH_CONFIDENCE),
|
||||
array('enabled', 'checkbox', Guess::HIGH_CONFIDENCE),
|
||||
array('id', 'integer', Guess::MEDIUM_CONFIDENCE),
|
||||
array('value', 'text', Guess::MEDIUM_CONFIDENCE),
|
||||
array('price', 'number', Guess::MEDIUM_CONFIDENCE),
|
||||
array('updated_at', 'datetime', Guess::HIGH_CONFIDENCE),
|
||||
|
||||
array('Authors', 'model', Guess::HIGH_CONFIDENCE, true),
|
||||
array('Resellers', 'model', Guess::HIGH_CONFIDENCE, true),
|
||||
array('MainAuthor', 'model', Guess::HIGH_CONFIDENCE, false),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue