From cb272f08bbf97f9caee93ec112a0b3f139974c31 Mon Sep 17 00:00:00 2001 From: William Espindola Date: Thu, 17 Sep 2015 15:23:46 -0300 Subject: [PATCH 1/5] Create Services encapsulate logic of relationa and doctrine --- .gitignore | 2 +- composer.json | 3 +- src/Extension/FieldTwigExtension.php | 29 ++++--- src/Service/DoctrineFieldService.php | 48 +++++++++++ src/Service/DoctrineOptionService.php | 38 +++++++++ src/Service/RespectFieldService.php | 48 +++++++++++ src/Service/RespectOptionService.php | 39 +++++++++ src/Storage/ORM/RespectRelational.php | 27 ++++--- ...on-twig-instance-and-get-field-object.phpt | 28 +++++++ .../Storage/ORM/RespectRelationalTest.php | 79 +++++++++++++++---- 10 files changed, 306 insertions(+), 35 deletions(-) create mode 100644 src/Service/DoctrineFieldService.php create mode 100644 src/Service/DoctrineOptionService.php create mode 100644 src/Service/RespectFieldService.php create mode 100644 src/Service/RespectOptionService.php create mode 100644 tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt diff --git a/.gitignore b/.gitignore index f02a2f8..751f6c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ build composer.lock docs -vendor +vendor \ No newline at end of file diff --git a/composer.json b/composer.json index c4108e1..2513e4f 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "require": { "php" : ">=5.4", "ocramius/generated-hydrator": "1.1.0", - "symfony/console": "~2.7" + "symfony/console": "~2.7", + "doctrine/orm": "2.5.0" }, "require-dev": { "phpunit/phpunit" : "4.*", diff --git a/src/Extension/FieldTwigExtension.php b/src/Extension/FieldTwigExtension.php index 0604742..52ddad0 100644 --- a/src/Extension/FieldTwigExtension.php +++ b/src/Extension/FieldTwigExtension.php @@ -6,7 +6,12 @@ use Twig_Function_Method; use WilliamEspindola\Field\Repository\FieldRepository; use WilliamEspindola\Field\Repository\OptionRepository; +use Respect\Relational\Mapper; +/** + * Class FieldTwigExtension + * @package WilliamEspindola\Field\Extension + */ class FieldTwigExtension extends Twig_Extension { /** @@ -19,12 +24,22 @@ class FieldTwigExtension extends Twig_Extension */ protected $optionService; + /** + * @param FieldRepository $fieldRepository + * @param OptionRepository $optionRepository + */ public function __construct( FieldRepository $fieldRepository, OptionRepository $optionRepository ) { $this->fieldRepository = $fieldRepository; $this->optionRepository = $optionRepository; + + if ($this->fieldRepository->getStorage()->getMapper() instanceof Mapper) { + $this->fieldService = new RespectFieldService($this->fieldRepository); + } else { + $this->fieldService = new DoctrineFieldService($this->fieldRepository); + } } /** @@ -45,7 +60,7 @@ public function getFunctions() */ public function getField($name) { - return $this->fieldRepository->findOne(['name' => $name]); + return $this->fieldService->findOneByName($name); } /** @@ -54,13 +69,9 @@ public function getField($name) */ public function getOptionsOfField($name) { - $field = $this->fieldRepository->findOne(['name' => $name]); - $field->options = $this->optionRepository->findBy( - ['field_id' => $field->getId()], - Sql::orderBy('id') - ); + $field = $this->fieldService->findOneByName($name); - return $field; + return $this->optionService->getOptionsOfField($field, 'id'); } /** @@ -69,9 +80,7 @@ public function getOptionsOfField($name) */ public function getFieldValue($name) { - $field = $this->fieldRepository->findOne(['name' => $name]); - - return $field->getValue(); + return $this->fieldService->findOneByNameAndGetValue($name); } /** diff --git a/src/Service/DoctrineFieldService.php b/src/Service/DoctrineFieldService.php new file mode 100644 index 0000000..9a028ef --- /dev/null +++ b/src/Service/DoctrineFieldService.php @@ -0,0 +1,48 @@ +repository = $repository; + } + + /** + * @param String $name + * @return Object WilliamEspindola\Field\Entity\Field + */ + public function findOneByName($name) + { + return $this->repository->findBy(['name' => $name], ['order' => 'name']); + } + + /** + * @param String $name + * @return String Value of field + */ + public function findOneByNameAndGetValue($name) + { + $fields = $this->repository->findBy(['name' => $name], ['order' => 'name']); + + if (!$fields) + return; + + return $fields[0]->getValue(); + } +} \ No newline at end of file diff --git a/src/Service/DoctrineOptionService.php b/src/Service/DoctrineOptionService.php new file mode 100644 index 0000000..8ec19f5 --- /dev/null +++ b/src/Service/DoctrineOptionService.php @@ -0,0 +1,38 @@ +repository = $repository; + } + + /** + * @param Object $field + * @param String $order + * @return ArrayObject Options of field + */ + public function getOptionsOfField($field, $order) + { + return $this->optionRepository->findBy( + ['field_id' => $field->getId()], + ['order' => $order] + ); + } +} \ No newline at end of file diff --git a/src/Service/RespectFieldService.php b/src/Service/RespectFieldService.php new file mode 100644 index 0000000..b4cf2c2 --- /dev/null +++ b/src/Service/RespectFieldService.php @@ -0,0 +1,48 @@ +repository = $repository; + } + + /** + * @param String $name + * @return Object Field object + */ + public function findOneByName($name) + { + return $this->repository->findBy(['name' => $name], ['order by name asc']); + } + + /** + * @param String $name + * @return String Value of field + */ + public function findOneByNameAndGetValue($name) + { + $fields = $this->repository->findBy(['name' => $name], ['order by name asc']); + + if (!$fields) + return; + + return $fields[0]->value; + } +} \ No newline at end of file diff --git a/src/Service/RespectOptionService.php b/src/Service/RespectOptionService.php new file mode 100644 index 0000000..a4c8d68 --- /dev/null +++ b/src/Service/RespectOptionService.php @@ -0,0 +1,39 @@ +repository = $repository; + } + + /** + * @param Object $field + * @param String $order + * @return ArrayObject Options of field + */ + public function getOptionsOfField($field, $order) + { + return $this->optionRepository->findBy( + ['field_id' => $field->id], + Sql::orderBy($order) + ); + } +} \ No newline at end of file diff --git a/src/Storage/ORM/RespectRelational.php b/src/Storage/ORM/RespectRelational.php index 49ed7d9..eabd04a 100644 --- a/src/Storage/ORM/RespectRelational.php +++ b/src/Storage/ORM/RespectRelational.php @@ -22,6 +22,11 @@ class RespectRelational implements StorageORMInterface */ protected $mapper; + /** + * @var string table name + */ + protected $repository; + public function __construct(Mapper $mapper) { $this->setMapper($mapper); @@ -46,23 +51,27 @@ public function getMapper() return $this->mapper; } - public function getRepository() - { - $repository = $this->repository; - $reflect = new \ReflectionClass(new $repository); - $tableName = strtolower($reflect->getShortName()); - return $this->getMapper()->$tableName; - } - public function setRepository($repository) { + if (empty($repository)) + throw new Argument('repository param can not be null'); + + if (class_exists($repository)) { + $reflect = new \ReflectionClass(new $repository); + $repository = strtolower($reflect->getShortName()); + } + $this->repository = $repository; return $this; } + public function getRepository() + { + return $this->getMapper()->$this->repository; + } + /** - * @param $tableName * @return array */ public function findAll() diff --git a/tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt b/tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt new file mode 100644 index 0000000..79d621a --- /dev/null +++ b/tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt @@ -0,0 +1,28 @@ +--TEST-- +Teste instance the FieldTEigExtension and get field Object +--FILE-- + 'pdo_sqlite', 'memory' => true]; +$configXml = __DIR__ . "../../../config/xml"; + +$setUp = Setup::createXMLMetadataConfiguration([$configXml], true); +$storage = new Doctrine($conn, $setUp); + +$fieldRepository = new FieldRepository($storage); +$optionRepository = new OptionRepository($storage); + +$extension = FieldTwigExtension($fieldRepository, $optionRepository); + +echo gettype($extension->getField('field-name')); +?> +--EXPECT-- +object \ No newline at end of file diff --git a/tests/unit/Storage/ORM/RespectRelationalTest.php b/tests/unit/Storage/ORM/RespectRelationalTest.php index 372c7a3..db0708d 100644 --- a/tests/unit/Storage/ORM/RespectRelationalTest.php +++ b/tests/unit/Storage/ORM/RespectRelationalTest.php @@ -2,13 +2,15 @@ use WilliamEspindola\Field\Storage\ORM\RespectRelational; +use Respect\Relational\Mapper; + class RespectRelationalTest extends \PHPUnit_Framework_TestCase { protected function setUp() { $this->mapper = $this->getMockBuilder('Respect\Relational\Mapper') - ->disableOriginalConstructor() - ->getMock(); + ->disableOriginalConstructor() + ->getMock(); $this->mapper->expects($this->any()) ->method(new PHPUnit_Framework_Constraint_IsAnything()) @@ -35,26 +37,75 @@ public function testSetMapperWithValidParameterShouldReturnMapperInGetMapper() ); } - public function testToDefineAnEntityNameSpaceForMapperWithValidDataShouldWork() + public function testGetMapperShouldReturnMockedInstance() { $instance = new RespectRelational($this->mapper); - $namespace = '\\My\Namespace\\Entity\\'; - $instance->setEntityNamespace($namespace); + $this->assertInstanceOf( + 'Respect\Relational\Mapper', + $instance->getMapper(), + 'The instance returned by getMapper is not instance of Respect\Relational\Mapper' + ); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetRepositoryWithInvalidValueShouldThrowAndException() + { + $instance = new RespectRelational($this->mapper); + + $instance->setRepository(''); + } + + public function testSetRepositoryWithAValidNamespaceShouldWork() + { + $instance = new RespectRelational($this->mapper); + $stubClass = $this->getMock('MyNamespace\MyClass'); + + $instance->setRepository('MyNamespace\MyClass'); $this->assertEquals( - $this->mapper->entityNamespace, - $namespace, - "Namespace isn't equals" + 'myclass', + PHPUnit_Framework_Assert::readAttribute($instance, 'repository'), + 'The attribute repository is not instance of the string class name: myclass' ); } - public function testGetMapperShouldReturnMockedInstance() + public function testSetRepositoryWithValidStringShouldWork() { - $instance = new RespectRelational($this->mapper); - $this->assertInstanceOf( - 'Respect\Relational\Mapper', - $instance->getMapper(), - 'The instance returned by getMapper is not instance of Respect\Relational\Mapper' + $instance = new RespectRelational($this->mapper); + + $instance->setRepository('mytable'); + + $this->assertEquals( + 'mytable', + PHPUnit_Framework_Assert::readAttribute($instance, 'repository'), + 'The attribute repository is not instance of the string table name: mytable' + ); + } + + public function testGetRepositoryShouldReturnMockedInstance() + { + $conn = $this->getMock( + 'PDO', + array('lastInsertId'), + array('sqlite::memory:') + ); + $conn->exec('CREATE TABLE mytable(id INTEGER PRIMARY KEY)'); + $conn->expects($this->any()) + ->method('lastInsertId') + ->will($this->throwException(new \PDOException)); + + + $mapper = new Mapper($conn); + + $instance = new RespectRelational($mapper); + $instance->setRepository('mytable'); + + $this->assertEquals( + 'mytable', + $instance->getRepository(), + 'is not mytable' ); } } \ No newline at end of file From 8b228a213c21001b8422f1090b3dea361ca2cad6 Mon Sep 17 00:00:00 2001 From: William Espindola Date: Thu, 17 Sep 2015 20:44:37 -0300 Subject: [PATCH 2/5] Refatoring Twig extension and create services to work than --- composer.json | 3 +- ...mEspindola.Field.Entity.Collection.dcm.xml | 2 +- ...ndola.Field.Entity.CollectionField.dcm.xml | 2 +- ...illiamEspindola.Field.Entity.Field.dcm.xml | 2 +- ...iamEspindola.Field.Entity.Options.dcm.xml} | 4 +-- src/Entity/{Option.php => Options.php} | 26 +++++++-------- src/Extension/FieldTwigExtension.php | 33 +++++-------------- src/Repository/OptionRepository.php | 2 +- src/Service/DoctrineFieldService.php | 12 +++++-- src/Service/DoctrineOptionService.php | 6 ++-- src/Service/RespectOptionService.php | 2 +- src/Storage/ORM/RespectRelational.php | 2 +- ...on-twig-instance-and-get-field-object.phpt | 26 +++++++++++---- ...mEspindola.Field.Entity.Collection.dcm.xml | 15 +++++++++ ...ndola.Field.Entity.CollectionField.dcm.xml | 15 +++++++++ ...illiamEspindola.Field.Entity.Field.dcm.xml | 17 ++++++++++ ...liamEspindola.Field.Entity.Options.dcm.xml | 16 +++++++++ 17 files changed, 128 insertions(+), 57 deletions(-) rename config/xml/{option.dcm.xml => WilliamEspindola.Field.Entity.Options.dcm.xml} (82%) rename src/Entity/{Option.php => Options.php} (57%) create mode 100644 tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Collection.dcm.xml create mode 100644 tests/functional/phpt/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml create mode 100644 tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Field.dcm.xml create mode 100644 tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Options.dcm.xml diff --git a/composer.json b/composer.json index 2513e4f..95207c6 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ "php" : ">=5.4", "ocramius/generated-hydrator": "1.1.0", "symfony/console": "~2.7", - "doctrine/orm": "2.5.0" + "doctrine/orm": "2.5.0", + "twig/twig": "1.9" }, "require-dev": { "phpunit/phpunit" : "4.*", diff --git a/config/xml/WilliamEspindola.Field.Entity.Collection.dcm.xml b/config/xml/WilliamEspindola.Field.Entity.Collection.dcm.xml index e363c65..652f433 100644 --- a/config/xml/WilliamEspindola.Field.Entity.Collection.dcm.xml +++ b/config/xml/WilliamEspindola.Field.Entity.Collection.dcm.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd"> - + diff --git a/config/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml b/config/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml index ef7c2ed..8048c96 100644 --- a/config/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml +++ b/config/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd"> - + diff --git a/config/xml/WilliamEspindola.Field.Entity.Field.dcm.xml b/config/xml/WilliamEspindola.Field.Entity.Field.dcm.xml index fd36f59..bdda98a 100644 --- a/config/xml/WilliamEspindola.Field.Entity.Field.dcm.xml +++ b/config/xml/WilliamEspindola.Field.Entity.Field.dcm.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd"> - + diff --git a/config/xml/option.dcm.xml b/config/xml/WilliamEspindola.Field.Entity.Options.dcm.xml similarity index 82% rename from config/xml/option.dcm.xml rename to config/xml/WilliamEspindola.Field.Entity.Options.dcm.xml index 027a4aa..5ef5399 100644 --- a/config/xml/option.dcm.xml +++ b/config/xml/WilliamEspindola.Field.Entity.Options.dcm.xml @@ -4,12 +4,12 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd"> - + - + diff --git a/src/Entity/Option.php b/src/Entity/Options.php similarity index 57% rename from src/Entity/Option.php rename to src/Entity/Options.php index e94d5c9..6d67df9 100644 --- a/src/Entity/Option.php +++ b/src/Entity/Options.php @@ -2,7 +2,7 @@ namespace WilliamEspindola\Field\Entity; -class Option implements EntityInterface +class Options implements EntityInterface { /** * @var integer @@ -12,27 +12,27 @@ class Option implements EntityInterface /** * @var text */ - private $option; + private $value; /** * $var object WilliamEspindola\Field\Entity\Field */ - private $field_id; + private $field; /** - * @param mixed $field_id + * @param mixed $field */ - public function setFieldId($field_id) + public function setField($field) { - $this->field_id = $field_id; + $this->field = $field; } /** * @return mixed */ - public function getFieldId() + public function getField() { - return $this->field_id; + return $this->field; } /** @@ -52,18 +52,18 @@ public function getId() } /** - * @param text $option + * @param text $value */ - public function setOption($option) + public function setValue($value) { - $this->option = $option; + $this->value = $value; } /** * @return text */ - public function getOption() + public function getValue() { - return $this->option; + return $this->value; } } \ No newline at end of file diff --git a/src/Extension/FieldTwigExtension.php b/src/Extension/FieldTwigExtension.php index 52ddad0..5818562 100644 --- a/src/Extension/FieldTwigExtension.php +++ b/src/Extension/FieldTwigExtension.php @@ -5,7 +5,8 @@ use Twig_Extension; use Twig_Function_Method; use WilliamEspindola\Field\Repository\FieldRepository; -use WilliamEspindola\Field\Repository\OptionRepository; +use WilliamEspindola\Field\Service\DoctrineFieldService; +use WilliamEspindola\Field\Service\RespectFieldService; use Respect\Relational\Mapper; /** @@ -15,30 +16,25 @@ class FieldTwigExtension extends Twig_Extension { /** - * @var \WilliamEspindola\Field\Repository\FieldRepository + * @var */ - protected $fieldRepository; + protected $fieldService; /** - * @var \WilliamEspindola\Field\Repository\OptionRepository + * @var */ protected $optionService; /** * @param FieldRepository $fieldRepository - * @param OptionRepository $optionRepository */ public function __construct( - FieldRepository $fieldRepository, - OptionRepository $optionRepository + FieldRepository $fieldRepository ) { - $this->fieldRepository = $fieldRepository; - $this->optionRepository = $optionRepository; - - if ($this->fieldRepository->getStorage()->getMapper() instanceof Mapper) { - $this->fieldService = new RespectFieldService($this->fieldRepository); + if ($fieldRepository->getStorage()->getMapper() instanceof Mapper) { + $this->fieldService = new RespectFieldService($fieldRepository); } else { - $this->fieldService = new DoctrineFieldService($this->fieldRepository); + $this->fieldService = new DoctrineFieldService($fieldRepository); } } @@ -63,17 +59,6 @@ public function getField($name) return $this->fieldService->findOneByName($name); } - /** - * @param $name Name of Field - * @return array Field Object with your options - */ - public function getOptionsOfField($name) - { - $field = $this->fieldService->findOneByName($name); - - return $this->optionService->getOptionsOfField($field, 'id'); - } - /** * @param $name Name of Field * @return string Value of field diff --git a/src/Repository/OptionRepository.php b/src/Repository/OptionRepository.php index 5502748..25b42c8 100644 --- a/src/Repository/OptionRepository.php +++ b/src/Repository/OptionRepository.php @@ -10,6 +10,6 @@ class OptionRepository { public function __construct(StorageORMInterface $storage) { - $this->setStorage($storage, 'WilliamEspindola\Field\Entity\Option'); + $this->setStorage($storage, 'WilliamEspindola\Field\Entity\Options'); } } \ No newline at end of file diff --git a/src/Service/DoctrineFieldService.php b/src/Service/DoctrineFieldService.php index 9a028ef..1854d36 100644 --- a/src/Service/DoctrineFieldService.php +++ b/src/Service/DoctrineFieldService.php @@ -29,7 +29,12 @@ public function __construct(RepositoryInterface $repository) */ public function findOneByName($name) { - return $this->repository->findBy(['name' => $name], ['order' => 'name']); + $field = $this->repository->findBy( + ['name' => $name], + [['name' => 'ASC'], null, null] + ); + + return $field ? $field[0] : false; } /** @@ -38,7 +43,10 @@ public function findOneByName($name) */ public function findOneByNameAndGetValue($name) { - $fields = $this->repository->findBy(['name' => $name], ['order' => 'name']); + $fields = $this->repository->findBy( + ['name' => $name], + [['name' => 'ASC'], null, null] + ); if (!$fields) return; diff --git a/src/Service/DoctrineOptionService.php b/src/Service/DoctrineOptionService.php index 8ec19f5..0636208 100644 --- a/src/Service/DoctrineOptionService.php +++ b/src/Service/DoctrineOptionService.php @@ -28,11 +28,11 @@ public function __costruct(RepositoryInterface $repository) * @param String $order * @return ArrayObject Options of field */ - public function getOptionsOfField($field, $order) + public function getOptionsOfField($field, Array $order) { - return $this->optionRepository->findBy( + return $this->repository->findBy( ['field_id' => $field->getId()], - ['order' => $order] + [$order, null, null] ); } } \ No newline at end of file diff --git a/src/Service/RespectOptionService.php b/src/Service/RespectOptionService.php index a4c8d68..ade3301 100644 --- a/src/Service/RespectOptionService.php +++ b/src/Service/RespectOptionService.php @@ -31,7 +31,7 @@ public function __costruct(RepositoryInterface $repository) */ public function getOptionsOfField($field, $order) { - return $this->optionRepository->findBy( + return $this->repository->findBy( ['field_id' => $field->id], Sql::orderBy($order) ); diff --git a/src/Storage/ORM/RespectRelational.php b/src/Storage/ORM/RespectRelational.php index eabd04a..b14a7bf 100644 --- a/src/Storage/ORM/RespectRelational.php +++ b/src/Storage/ORM/RespectRelational.php @@ -58,7 +58,7 @@ public function setRepository($repository) if (class_exists($repository)) { $reflect = new \ReflectionClass(new $repository); - $repository = strtolower($reflect->getShortName()); + $repository = strtolower($reflect->getShowrtName()); } $this->repository = $repository; diff --git a/tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt b/tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt index 79d621a..a3c42a5 100644 --- a/tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt +++ b/tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt @@ -6,23 +6,37 @@ require 'vendor/autoload.php'; use WilliamEspindola\Field\Extension\FieldTwigExtension; use WilliamEspindola\Field\Repository\FieldRepository; -use WilliamEspindola\Field\Repository\OptionRepository; -use WilliamEspindola\Field\Storage\ORM\RespectRelational; use Doctrine\ORM\Tools\Setup; use WilliamEspindola\Field\Storage\ORM\Doctrine; -$conn = ['driver' => 'pdo_sqlite', 'memory' => true]; -$configXml = __DIR__ . "../../../config/xml"; +$conn = ['driver' => 'pdo_mysql', 'user' => 'root', 'password' => '123', 'dbname' => 'field']; +$configXml = __DIR__ . "/xml/"; $setUp = Setup::createXMLMetadataConfiguration([$configXml], true); $storage = new Doctrine($conn, $setUp); +$stmt = $storage->getMapper()->getConnection()->prepare(" +CREATE TABLE IF NOT EXISTS field ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + type TEXT NOT NULL, + value TEXT NULL, + label TEXT NOT NULL +); +INSERT INTO field (id, name, type, value, label) VALUES (1, 'field-name', 'text', 'field-name', 'field-name'); +"); +$stmt->execute(); +$stmt->closeCursor(); + $fieldRepository = new FieldRepository($storage); -$optionRepository = new OptionRepository($storage); -$extension = FieldTwigExtension($fieldRepository, $optionRepository); +$extension = new FieldTwigExtension($fieldRepository); echo gettype($extension->getField('field-name')); + +$stmt = $storage->getMapper()->getConnection()->prepare("DROP TABLE field;"); +$stmt->execute(); +$stmt->closeCursor(); ?> --EXPECT-- object \ No newline at end of file diff --git a/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Collection.dcm.xml b/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Collection.dcm.xml new file mode 100644 index 0000000..652f433 --- /dev/null +++ b/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Collection.dcm.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml b/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml new file mode 100644 index 0000000..8048c96 --- /dev/null +++ b/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Field.dcm.xml b/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Field.dcm.xml new file mode 100644 index 0000000..bdda98a --- /dev/null +++ b/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Field.dcm.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Options.dcm.xml b/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Options.dcm.xml new file mode 100644 index 0000000..5ef5399 --- /dev/null +++ b/tests/functional/phpt/xml/WilliamEspindola.Field.Entity.Options.dcm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + \ No newline at end of file From d78c098220ace8130e11a4bec70fec31009b3297 Mon Sep 17 00:00:00 2001 From: William Espindola Date: Thu, 17 Sep 2015 21:11:55 -0300 Subject: [PATCH 3/5] Create testes to getField with respect relationa --- composer.json | 4 +- src/Service/RespectFieldService.php | 4 +- src/Storage/ORM/RespectRelational.php | 4 +- ...-and-get-field-object-using-doctrine.phpt} | 0 ...ce-and-get-field-object-using-respect.phpt | 37 +++++++++++++++++++ 5 files changed, 43 insertions(+), 6 deletions(-) rename tests/functional/phpt/{1-extension-twig-instance-and-get-field-object.phpt => 1-extension-twig-instance-and-get-field-object-using-doctrine.phpt} (100%) create mode 100644 tests/functional/phpt/1-extension-twig-instance-and-get-field-object-using-respect.phpt diff --git a/composer.json b/composer.json index 95207c6..c4108e1 100644 --- a/composer.json +++ b/composer.json @@ -20,9 +20,7 @@ "require": { "php" : ">=5.4", "ocramius/generated-hydrator": "1.1.0", - "symfony/console": "~2.7", - "doctrine/orm": "2.5.0", - "twig/twig": "1.9" + "symfony/console": "~2.7" }, "require-dev": { "phpunit/phpunit" : "4.*", diff --git a/src/Service/RespectFieldService.php b/src/Service/RespectFieldService.php index b4cf2c2..04c6cd4 100644 --- a/src/Service/RespectFieldService.php +++ b/src/Service/RespectFieldService.php @@ -29,7 +29,9 @@ public function __construct(RepositoryInterface $repository) */ public function findOneByName($name) { - return $this->repository->findBy(['name' => $name], ['order by name asc']); + $field = $this->repository->findBy(['name' => $name], ['order by name asc']); + + return $field ? $field[0] : false; } /** diff --git a/src/Storage/ORM/RespectRelational.php b/src/Storage/ORM/RespectRelational.php index b14a7bf..566b26d 100644 --- a/src/Storage/ORM/RespectRelational.php +++ b/src/Storage/ORM/RespectRelational.php @@ -58,7 +58,7 @@ public function setRepository($repository) if (class_exists($repository)) { $reflect = new \ReflectionClass(new $repository); - $repository = strtolower($reflect->getShowrtName()); + $repository = strtolower($reflect->getShortName()); } $this->repository = $repository; @@ -68,7 +68,7 @@ public function setRepository($repository) public function getRepository() { - return $this->getMapper()->$this->repository; + return $this->getMapper()->{$this->repository}; } /** diff --git a/tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt b/tests/functional/phpt/1-extension-twig-instance-and-get-field-object-using-doctrine.phpt similarity index 100% rename from tests/functional/phpt/1-extension-twig-instance-and-get-field-object.phpt rename to tests/functional/phpt/1-extension-twig-instance-and-get-field-object-using-doctrine.phpt diff --git a/tests/functional/phpt/1-extension-twig-instance-and-get-field-object-using-respect.phpt b/tests/functional/phpt/1-extension-twig-instance-and-get-field-object-using-respect.phpt new file mode 100644 index 0000000..21720b1 --- /dev/null +++ b/tests/functional/phpt/1-extension-twig-instance-and-get-field-object-using-respect.phpt @@ -0,0 +1,37 @@ +--TEST-- +Teste instance the FieldTEigExtension and get field Object +--FILE-- +query(" +CREATE TABLE IF NOT EXISTS field ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + type TEXT NOT NULL, + value TEXT NULL, + label TEXT NOT NULL +); +INSERT INTO field (id, name, type, value, label) VALUES (1, 'field-name', 'text', 'field-name', 'field-name'); +")->exec(); + +$fieldRepository = new FieldRepository($storage); + +$extension = new FieldTwigExtension($fieldRepository); + +echo gettype($extension->getField('field-name')); + +$db->query("DROP TABLE field;")->exec(); +?> +--EXPECT-- +object \ No newline at end of file From 8f02e88770a7fb12e4bb8c3a3e560aef5ce125f2 Mon Sep 17 00:00:00 2001 From: William Espindola Date: Thu, 17 Sep 2015 21:14:04 -0300 Subject: [PATCH 4/5] Teste integration to retrive field value with respect and doctrine --- ...ce-and-get-field-value-using-doctrine.phpt | 42 +++++++++++++++++++ ...nce-and-get-field-value-using-respect.phpt | 37 ++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 tests/functional/phpt/1-extension-twig-instance-and-get-field-value-using-doctrine.phpt create mode 100644 tests/functional/phpt/1-extension-twig-instance-and-get-field-value-using-respect.phpt diff --git a/tests/functional/phpt/1-extension-twig-instance-and-get-field-value-using-doctrine.phpt b/tests/functional/phpt/1-extension-twig-instance-and-get-field-value-using-doctrine.phpt new file mode 100644 index 0000000..e881b9e --- /dev/null +++ b/tests/functional/phpt/1-extension-twig-instance-and-get-field-value-using-doctrine.phpt @@ -0,0 +1,42 @@ +--TEST-- +Teste instance the FieldTEigExtension and get field Object +--FILE-- + 'pdo_mysql', 'user' => 'root', 'password' => '123', 'dbname' => 'field']; +$configXml = __DIR__ . "/xml/"; + +$setUp = Setup::createXMLMetadataConfiguration([$configXml], true); +$storage = new Doctrine($conn, $setUp); + +$stmt = $storage->getMapper()->getConnection()->prepare(" +CREATE TABLE IF NOT EXISTS field ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + type TEXT NOT NULL, + value TEXT NULL, + label TEXT NOT NULL +); +INSERT INTO field (id, name, type, value, label) VALUES (1, 'field-name', 'text', 'field-name', 'field-name'); +"); +$stmt->execute(); +$stmt->closeCursor(); + +$fieldRepository = new FieldRepository($storage); + +$extension = new FieldTwigExtension($fieldRepository); + +echo $extension->getFieldValue('field-name'); + +$stmt = $storage->getMapper()->getConnection()->prepare("DROP TABLE field;"); +$stmt->execute(); +$stmt->closeCursor(); +?> +--EXPECT-- +field-name \ No newline at end of file diff --git a/tests/functional/phpt/1-extension-twig-instance-and-get-field-value-using-respect.phpt b/tests/functional/phpt/1-extension-twig-instance-and-get-field-value-using-respect.phpt new file mode 100644 index 0000000..e8700b6 --- /dev/null +++ b/tests/functional/phpt/1-extension-twig-instance-and-get-field-value-using-respect.phpt @@ -0,0 +1,37 @@ +--TEST-- +Teste instance the FieldTEigExtension and get field Object +--FILE-- +query(" +CREATE TABLE IF NOT EXISTS field ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + type TEXT NOT NULL, + value TEXT NULL, + label TEXT NOT NULL +); +INSERT INTO field (id, name, type, value, label) VALUES (1, 'field-name', 'text', 'field-name', 'field-name'); +")->exec(); + +$fieldRepository = new FieldRepository($storage); + +$extension = new FieldTwigExtension($fieldRepository); + +echo $extension->getFieldValue('field-name'); + +$db->query("DROP TABLE field;")->exec(); +?> +--EXPECT-- +field-name \ No newline at end of file From 695f1464b74b8ed57063aac87611f7aec1b1c69b Mon Sep 17 00:00:00 2001 From: William Espindola Date: Thu, 17 Sep 2015 21:18:39 -0300 Subject: [PATCH 5/5] Rename collection_field to collection field and remove MER --- ...ndola.Field.Entity.CollectionField.dcm.xml | 2 +- data/mysql-schema.sql | 47 ------------------ data/schema-0.3.mwb | Bin 9745 -> 0 bytes data/schema-0.3.mwb.bak | Bin 9641 -> 0 bytes data/schema-0.3.sql | 4 +- ...ndola.Field.Entity.CollectionField.dcm.xml | 2 +- 6 files changed, 4 insertions(+), 51 deletions(-) delete mode 100644 data/mysql-schema.sql delete mode 100644 data/schema-0.3.mwb delete mode 100644 data/schema-0.3.mwb.bak diff --git a/config/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml b/config/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml index 8048c96..4d057d6 100644 --- a/config/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml +++ b/config/xml/WilliamEspindola.Field.Entity.CollectionField.dcm.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://raw.github.com/doctrine/doctrine2/master/doctrine-mapping.xsd"> - + diff --git a/data/mysql-schema.sql b/data/mysql-schema.sql deleted file mode 100644 index d781ab5..0000000 --- a/data/mysql-schema.sql +++ /dev/null @@ -1,47 +0,0 @@ -CREATE TABLE IF NOT EXISTS `field` ( - `id` INT NOT NULL AUTO_INCREMENT, - `name` VARCHAR(255) NOT NULL, - `type` VARCHAR(255) NOT NULL, - `value` TEXT NULL, - `label` VARCHAR(255) NOT NULL, - PRIMARY KEY (`id`)) - ENGINE = InnoDB; - -CREATE TABLE IF NOT EXISTS `option` ( - `id` INT NOT NULL, - `option` TEXT NULL, - `field_id` INT NOT NULL, - PRIMARY KEY (`id`), - INDEX `fk_options_field_idx` (`field_id` ASC), - CONSTRAINT `fk_options_field` - FOREIGN KEY (`field_id`) - REFERENCES `field` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) - ENGINE = InnoDB; - -CREATE TABLE IF NOT EXISTS `collection` ( - `id` INT NOT NULL AUTO_INCREMENT, - `name` VARCHAR(255) NOT NULL, - `label` VARCHAR(255) NOT NULL, - PRIMARY KEY (`id`)) - ENGINE = InnoDB; - -CREATE TABLE IF NOT EXISTS `collectionfield` ( - `collection_id` INT NOT NULL, - `field_id` INT NOT NULL, - `id` INT NOT NULL AUTO_INCREMENT, - PRIMARY KEY (`id`, `collection_id`, `field_id`), - INDEX `fk_collection_has_field_field1_idx` (`field_id` ASC), - INDEX `fk_collection_has_field_collection1_idx` (`collection_id` ASC), - CONSTRAINT `fk_collection_has_field_collection1` - FOREIGN KEY (`collection_id`) - REFERENCES `collection` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION, - CONSTRAINT `fk_collection_has_field_field1` - FOREIGN KEY (`field_id`) - REFERENCES `field` (`id`) - ON DELETE NO ACTION - ON UPDATE NO ACTION) - ENGINE = InnoDB; \ No newline at end of file diff --git a/data/schema-0.3.mwb b/data/schema-0.3.mwb deleted file mode 100644 index 67a60d92bed07d4afe2c7091b335fb90f391efe4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9745 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 diff --git a/data/schema-0.3.mwb.bak b/data/schema-0.3.mwb.bak deleted file mode 100644 index c283dc782258b9ab2908d35b27abb35096540933..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9641 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 - +