Symfony 2.3 cascade_validation - validation

i build a form in symfony 2.3. Which shows and save user profile and addresses for this profile.
I have two entities. A user and a address entity. (I will only show some code parts, this code is not complete here)
User Entity:
/**
* #ORM\Entity
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Ebm\UserBundle\Entity\UserRepository")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
protected $firstName;
/**
* #ORM\Column(type="string", length=255)
*/
protected $lastName;
/**
* #ORM\Column(type="string", length=255, unique=true)
*/
private $username;
/**
* #var \Doctrine\Common\Collections\Collection
* #Assert\Valid
* #ORM\ManyToMany(targetEntity="Ebm\UserBundle\Entity\Address", cascade={"persist", "remove"})
* #ORM\JoinTable(name="user_address",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="address_id", referencedColumnName="id")}
* )
*/
protected $addresses;
}
Address Entity:
/**
* #ORM\Entity
* #ORM\Table(name="address")
*/
class Address
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
protected $street;
}
And a form which embed the address entity as collection into the user form "usertype".
Usertype:
class UserType extends AbstractType
{
protected $securityContext;
public function __construct(SecurityContext $securityContext)
{
$this->securityContext = $securityContext;
}
/**
* (non-PHPdoc)
* #see \Symfony\Component\Form\AbstractType::buildForm()
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->->add('addresses', 'collection', array(
'type' => new AddressType() ,
'allow_add' => true,
'allow_delete' => true,
'label' => false,
'cascade_validation' => true)
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ebm\UserBundle\Entity\User',
'cascade_validation' => true,
));
}
public function getName()
{
return 'user';
}
}
In my controller the validation part look like this:
$form = $this->createForm($this->get('form.type.user'), $user)->add('save', 'submit');
// On initial page load handleRequest() recognizes that the form was not submitted and does nothing
// isValid returns false
$form->handleRequest($request);
// Check if form isValid
if ($form->isValid()) {
}
And my validation yml (UserBundle/config/validation.yml) file look like this
Ebm\UserBundle\Entity\User:
properties:
firstName:
- NotBlank: ~
- Length:
max: 255
addresses:
- Valid: ~
Ebm\UserBundle\Entity\Address:
properties:
street:
- NotBlank: { message: "validate.not_blank" }
- Type:
type: string
- Length:
max: 255
If the address field is emtyp "street" as example no error was occured. Only validation errors for
entity "user" is displayed.
I'm looking since a week for a solution, i will be very lucky if anyone can help me

As of >= Symfony 2.5 you can use the deep parameter of getErrors
If you want to display errors for compound forms use the deep parameter in the getErrors() function:
// Check if form isValid
if ($form->isValid() === false) {
echo $form->getErrors(true);
}
Otherwise use the all function recursive:
public function getErrorsRecursive($form) {
$errors = array();
foreach ($form->getErrors() as $error) {
$errors[] = $error->getMessage();
}
foreach ($form->all() as $fieldName => $formType) {
if ($errorMessage = $this->getErrorsRecursive($formType)) {
$errors[$fieldName] = array_pop($errorMessage);
}
}
return $errors;
}

Related

nullable not being read in Doctrine

I have a Zend Expressive project from Skeleton app. Also, the database NOT NULL is set for each of the fields that I have set nullable on. I think it might have something to do with it not reading the annotations. I had issues getting the annotations working in the first place, especially for the cli-config.php
Here's the DoctrineFactory I created loosely based on one I found as an example. I changed the way it creates the entityManager to more closely represent the Doctrine docs config example.
public function __invoke(ContainerInterface $container)
{
$config = $container->has('config') ? $container->get('config') : [];
$proxyDir = (isset($config['doctrine']['orm']['proxy_dir'])) ?
$config['doctrine']['orm']['proxy_dir'] : 'data/cache/EntityProxy';
$proxyNamespace = (isset($config['doctrine']['orm']['proxy_namespace'])) ?
$config['doctrine']['orm']['proxy_namespace'] : 'EntityProxy';
$autoGenerateProxyClasses = (isset($config['doctrine']['orm']['auto_generate_proxy_classes'])) ?
$config['doctrine']['orm']['auto_generate_proxy_classes'] : false;
$underscoreNamingStrategy = (isset($config['doctrine']['orm']['underscore_naming_strategy'])) ?
$config['doctrine']['orm']['underscore_naming_strategy'] : false;
$paths = (isset($config['doctrine']['paths'])) ? $config['doctrine']['paths'] : [];
$isDevMode = (isset($config['doctrine']['isDevMode'])) ? $config['doctrine']['isDevMode'] : false;
$doctrine = Setup::createAnnotationMetadataConfiguration($paths, $isDevMode);
// Doctrine ORM
$doctrine->setProxyDir($proxyDir);
$doctrine->setProxyNamespace($proxyNamespace);
$doctrine->setAutoGenerateProxyClasses($autoGenerateProxyClasses);
if ($underscoreNamingStrategy) {
$doctrine->setNamingStrategy(new UnderscoreNamingStrategy());
}
// Cache
$cache = $container->get(Cache::class);
$doctrine->setQueryCacheImpl($cache);
$doctrine->setResultCacheImpl($cache);
$doctrine->setMetadataCacheImpl($cache);
// EntityManager
return EntityManager::create($config['doctrine']['connection']['orm_default'], $doctrine);
}
Config like so:
'doctrine' => [
'orm' => [
'auto_generate_proxy_classes' => false,
'proxy_dir' => 'data/cache/EntityProxy',
'proxy_namespace' => 'EntityProxy',
'underscore_naming_strategy' => true,
],
'connection' => [
// default connection
'orm_default' => [
'driver' => 'pdo_mysql',
'host' => '127.0.0.1',
'port' => '3306',
'dbname' => 'users',
'user' => 'root',
'password' => 'password',
'charset' => 'UTF8',
],
],
'paths' => [
__DIR__.'/../../vendor/plexus/user-lib/src/Entity'
],
'isDevMode' => false,
'cache' => [
'redis' => [
'host' => '127.0.0.1',
'port' => '6379',
],
],
],
Entity:
namespace Plexus\User\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class User
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(name="id", type="int")
* #var int
*/
protected $id;
/**
* #ORM\Column(name="email", type="string", length=255)
* #var string
*/
protected $email;
/**
* #ORM\Column(name="unverifiedEmail", type="string", length=255, nullable=true)
* #var string
*/
protected $unverifiedEmail;
/**
* #ORM\Column(name="unverifiedEmailHash", type="string", length=255, nullable=true)
* #var string
*/
protected $verifyEmailHash;
/**
* #var string
* At this time, http://php.net/manual/en/function.password-hash.php recommends using 255 length for hashes
* #ORM\Column(name="passwordHash", type="string", length=255)
*/
protected $passwordHash;
/**
* #var string
* #ORM\Column(name="passwordResetHash", type="string", length=255, nullable=true)
*/
protected $passwordResetHash;
/**
* #return mixed
*/
public function getUnverifiedEmail()
{
return $this->unverifiedEmail;
}
/**
* #param mixed $unverifiedEmail
*/
public function setUnverifiedEmail($unverifiedEmail)
{
$this->unverifiedEmail = $unverifiedEmail;
}
/**
* #return mixed
*/
public function getVerifyEmailHash()
{
return $this->verifyEmailHash;
}
/**
* #param mixed $verifyEmailHash
*/
public function setVerifyEmailHash($verifyEmailHash)
{
$this->verifyEmailHash = $verifyEmailHash;
}
/**
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* #param string $email
*/
public function setEmail($email)
{
$this->email = $email;
}
/**
* #return string
*/
public function getPasswordHash()
{
return $this->passwordHash;
}
/**
* #param string $passwordHash
*/
public function setPasswordHash($passwordHash)
{
$this->passwordHash = $passwordHash;
}
/**
* #return string
*/
public function getPasswordResetHash(): string
{
return $this->passwordResetHash;
}
/**
* #param string $passwordResetHash
*/
public function setPasswordResetHash(string $passwordResetHash)
{
$this->passwordResetHash = $passwordResetHash;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
public function toArray()
{
return [
'email' => $this->getEmail(),
];
}
}
Error:
Doctrine\DBAL\Exception\NotNullConstraintViolationException: An exception occurred while executing 'INSERT INTO user (unverifiedEmail, unverifiedEmailHash, passwordHash, passwordResetHash) VALUES (?, ?, ?, ?)' with params [null, null, "$2y$10$pRDv8NFXaCxF7\/ZUzL.ZuulsFqdwTs9IOycWTHYA.1Q0qpFu5uGXe", null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'unverifiedEmail' cannot be null in file /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php on line 118
Stack trace:
1. Doctrine\DBAL\Exception\NotNullConstraintViolationException->() /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php:118
2. Doctrine\DBAL\Driver\AbstractMySQLDriver->convertException() /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php:176
3. Doctrine\DBAL\DBALException->wrapException() /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php:150
4. Doctrine\DBAL\DBALException->driverExceptionDuringQuery() /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:177
5. Doctrine\DBAL\Driver\PDOException->() /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:107
6. PDOException->() /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:105
7. PDOStatement->execute() /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:105
8. Doctrine\DBAL\Driver\PDOStatement->execute() /var/www/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:168
9. Doctrine\DBAL\Statement->execute() /var/www/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:283
10. Doctrine\ORM\Persisters\Entity\BasicEntityPersister->executeInserts() /var/www/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1051
11. Doctrine\ORM\UnitOfWork->executeInserts() /var/www/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:386
12. Doctrine\ORM\UnitOfWork->commit() /var/www/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:358
13. Doctrine\ORM\EntityManager->flush() /var/www/vendor/plexus/user-lib/src/Service/UserServiceDoctrine.php:53
14. Plexus\User\Service\UserServiceDoctrine->saveUser() /var/www/vendor/plexus/user-lib/src/Service/UserService.php:27
15. Plexus\User\Service\UserService->setPassword() /var/www/vendor/plexus/user-lib/src/Service/UserServiceDoctrine.php:43
16. Plexus\User\Service\UserServiceDoctrine->createUser() /var/www/src/App/src/Action/CreateUserAction.php:39
17. App\Action\CreateUserAction->process() /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php:62
...
Any help would be greatly appreciated. I can't think of what would cause this.
So the issue, it turns out, is that Doctrine was caching my entities and probably had a hold of a stale entity. I figured this out because I added the id field but it wasn't showing up at all. I destroyed and recreated my Vagrant box, and it worked.
So I added this if statement around the cache adapter:
if (!$isDevMode) {
// Cache
$cache = $container->get(Cache::class);
$doctrine->setQueryCacheImpl($cache);
$doctrine->setResultCacheImpl($cache);
$doctrine->setMetadataCacheImpl($cache);
}
and I set $isDevMode to true.

CollectionType with multiple entity type + Ajax

I have a problem creating a form by including a formType that are 2 related entities and that must be combined with a CollectionType.
I use the library jquery.collection for CollectionType ( http://symfony-collection.fuz.org/symfony3/ )
My entities
Product
namespace BBW\ProductBundle\Entity\Product;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Product
*
* #ORM\Table(name="product_product")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Product\ProductRepository")
*/
class Product
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\AttributeGroup", inversedBy="products", fetch="EAGER")
* #ORM\JoinTable(name="product_join_attribute_group")
*/
private $attributeGroups;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\Attribute", inversedBy="products", fetch="EAGER")
* #ORM\JoinTable(name="product_join_attribute")
*/
private $attributes;
/**
* Constructor
*/
public function __construct()
{
$this->attributeGroups = new \Doctrine\Common\Collections\ArrayCollection();
$this->attributes = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*
* #return Product
*/
public function addAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup)
{
$this->attributeGroups[] = $attributeGroup;
return $this;
}
/**
* Remove attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*/
public function removeAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup)
{
$this->attributeGroups->removeElement($attributeGroup);
}
/**
* Get attributeGroups
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributeGroups()
{
return $this->attributeGroups;
}
/**
* Add attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*
* #return Product
*/
public function addAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes[] = $attribute;
return $this;
}
/**
* Remove attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*/
public function removeAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes->removeElement($attribute);
}
/**
* Get attributes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributes()
{
return $this->attributes;
}
}
AttributeGroup
namespace BBW\ProductBundle\Entity\Attribute;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* AttributeGroup
*
* #ORM\Table(name="product_attribute_group")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Attribute\AttributeGroupRepository")
*/
class AttributeGroup
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\OneToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\Attribute", mappedBy="attributeGroup", cascade={"persist", "remove"}, fetch="EAGER")
* #ORM\JoinColumn(name="attribute_id")
*/
private $attributes ;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Product\Product", mappedBy="attributeGroups", cascade={"persist"}, fetch="EAGER")
*/
private $products;
/**
* Constructor
*/
public function __construct()
{
$this->attributes = new \Doctrine\Common\Collections\ArrayCollection();
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*
* #return AttributeGroup
*/
public function addAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes[] = $attribute;
return $this;
}
/**
* Remove attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*/
public function removeAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes->removeElement($attribute);
}
/**
* Get attributes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Add product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*
* #return AttributeGroup
*/
public function addProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products[] = $product;
return $this;
}
/**
* Remove product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*/
public function removeProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Attributes
namespace BBW\ProductBundle\Entity\Attribute;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Attribute
*
* #ORM\Table(name="product_attribute")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Attribute\AttributeRepository")
*/
class Attribute
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="BBW\ProductBundle\Entity\Attribute\AttributeGroup", inversedBy="attributes", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinColumn(name="attribute_group_id")
*/
private $attributeGroup ;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Product\Product", mappedBy="attributes", cascade={"persist"}, fetch="EAGER")
*/
private $products;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*
* #return Attribute
*/
public function setAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup = null)
{
$this->attributeGroup = $attributeGroup;
return $this;
}
/**
* Get attributeGroup
*
* #return \BBW\ProductBundle\Entity\Attribute\AttributeGroup
*/
public function getAttributeGroup()
{
return $this->attributeGroup;
}
/**
* Constructor
*/
public function __construct()
{
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*
* #return Attribute
*/
public function addProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products[] = $product;
return $this;
}
/**
* Remove product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*/
public function removeProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Explanation
My product must belong to an attribute group and must contain attributes. I have a relationship between my AttributeGroup entity and a Relationship with Attribute.
My Entity AttributeGroup has a relationship with Attribute (functional).
Form Types
In my form I have a CollectionType with another formType as entry_type I have created with two entity types (AttributeGroups / Attributes)
FormType for product
namespace BBW\ProductBundle\Form\Product;
use A2lix\TranslationFormBundle\Form\Type\TranslationsType;
use BBW\CoreBundle\FormTypes\SwitchType;
use BBW\MediaBundle\Transformers\MediaTransformer;
use BBW\ProductBundle\Form\Attribute\Type\AttributeType;
use Doctrine\ORM\Mapping\Entity;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProductEditType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$locale = $options['int_service'];
$builder
->add('attributes', CollectionType::class, array(
'entry_type' => AttributeType::class,
'entry_options' => array(
'label' => false
),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'attr' => array(
'class' => 'attributes-selector'
),
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BBW\ProductBundle\Entity\Product\Product',
'int_service' => ['fr'],
'translation_domain' => 'ProductBundle'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'bbw_productbundle_edit_product';
}
}
AttributeType
/**
* Form Type: Fields add in product edit form
*/
namespace BBW\ProductBundle\Form\Attribute\Type;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AttributeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('attributeGroups', EntityType::class, array(
'label' => false,
'class' => 'BBW\ProductBundle\Entity\Attribute\AttributeGroup',
'choice_label' => 'translate.name',
'attr' => array(
'class' => 'choiceAttributeGroup'
),
))
->add('attributes', EntityType::class, array(
'label' => false,
'class' => 'BBW\ProductBundle\Entity\Attribute\Attribute',
'choice_label' => 'translate.name',
'attr' => array(
'class' => 'choiceAttributes'
)
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BBW\ProductBundle\Entity\Product\Product',
'int_service' => ['fr'],
'translation_domain' => 'ProductBundle'
));
}
}
Explanation
The two entities (AttributeGroups / Attributes) are two linked drop-down lists. When I select a group, I must display the data of this group in the second drop-down list. This part works well. I can duplicate as much as I want (with the CollectionType).
First problem when i submit the form:
"Could not determine access type for property "attributeGroups" ".
Second problem when i set "mapped => false" in my two entities:
"Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "BBW\ProductBundle\Entity\Product\Product#$attributes", got "BBW\ProductBundle\Entity\Product\Product" instead."
Screeshoots dump/form
Conclusion
I think my problem is a mapping problem, having tested a lot of code and explanation on the symfony doc or other site, I could not solve my problem. If anyone could help me out and explain the good work of a collectionType with 2 entity and if that is possible. If you have examples of codes I am taker too.
Thank you in advance for your help.

Uploading images with symfony. The image is not saved in the correct path

I am new at Symfony. I have been trying to create a reusable form and upload an image with it. The problem is that the image it is not saved in the path i have given. I dont know what i am doing wrong. I would really appreciate any suggestion.
Entity
<?php
namespace test\TestBundle\Entity\media;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\validator\Constraints as Assert;
/**
* Media
*#ORM\Entity
* #ORM\Table(name="upload")
* #ORM\HasLifecycleCallbacks
*/
class Media
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="name",type="string",length=255)
* #Assert\NotBlank
*/
public $name;
/**
* #ORM\Column(name="path",type="string",length=255, nullable=true)
*/
public $path;
public $file;
/**
* #ORM\PostLoad()
*/
public function postLoad()
{
$this->updateAt = new \DateTime();
}
public function getUploadRootDir()
{
return __dir__.'/../../../../web/uploads';
}
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
public function getAssetPath()
{
return 'uploads/'.$this->path;
}
/**
* #ORM\Prepersist()
* #ORM\Preupdate()
*/
public function preUpload()
{
$this->tempFile = $this->getAbsolutePath();
$this->oldFile = $this->getPath();
$this->updateAt = new \DateTime();
if (null !== $this->file)
$this->path = sha1(uniqid(mt_rand(),true)).'.'.$this->file->guessExtension();
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null !== $this->file) {
$this->file->move($this->getUploadRootDir(),$this->path);
unset($this->file);
if ($this->oldFile != null) unlink($this->tempFile);
}
}
/**
* #ORM\PreRemove()
*/
public function preRemoveUpload()
{
$this->tempFile = $this->getAbsolutePath();
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if (file_exists($this->tempFile)) unlink($this->tempFile);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function getPath()
{
return $this->path;
}
public function getName()
{
var_dump($this->name);
return $this->name;
}
}
Controller
namespace test\TestBundle\Controller\media;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use test\TestBundle\Entity\media\Media;
use test\TestBundle\Form\media\MediaType;
/**
* media\Media controller.
*
* #Route("/img")
*/
class MediaController extends Controller
{
/**
* Lists all media\Media entities.
*
* #Route("/", name="img")
* #Method("GET")
* #Template()
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('testTestBundle:media\Media')->findAll();
return array(
'entities' => $entities,
);
}
/**
* Creates a new media\Media entity.
*
* #Route("/", name="img_create")
* #Method("POST")
* #Template("testTestBundle:media\Media:new.html.twig")
*/
public function createAction(Request $request)
{
$entity = new Media();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('img_show', array('id' => $entity->getId())));
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
/**
* Creates a form to create a media\Media entity.
*
* #param Media $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(Media $entity)
{
$form = $this->createForm(new MediaType(), $entity, array(
'action' => $this->generateUrl('img_create'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Create'));
return $form;
}
/**
* Displays a form to create a new media\Media entity.
*
* #Route("/new", name="img_new")
* #Method("GET")
* #Template()
*/
public function newAction()
{
$entity = new Media();
$form = $this->createCreateForm($entity);
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
/**
* Finds and displays a media\Media entity.
*
* #Route("/{id}", name="img_show")
* #Method("GET")
* #Template()
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('testTestBundle:media\Media')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find media\Media entity.');
}
$deleteForm = $this->createDeleteForm($id);
return array(
'entity' => $entity,
'delete_form' => $deleteForm->createView(),
);
}
/**
* Displays a form to edit an existing media\Media entity.
*
* #Route("/{id}/edit", name="img_edit")
* #Method("GET")
* #Template()
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('testTestBundle:media\Media')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find media\Media entity.');
}
$editForm = $this->createEditForm($entity);
$deleteForm = $this->createDeleteForm($id);
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
/**
* Creates a form to edit a media\Media entity.
*
* #param Media $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createEditForm(Media $entity)
{
$form = $this->createForm(new MediaType(), $entity, array(
'action' => $this->generateUrl('img_update', array('id' => $entity->getId())),
'method' => 'PUT',
));
$form->add('submit', 'submit', array('label' => 'Update'));
return $form;
}
/**
* Edits an existing media\Media entity.
*
* #Route("/{id}", name="img_update")
* #Method("PUT")
* #Template("testTestBundle:media\Media:edit.html.twig")
*/
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('testTestBundle:media\Media')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find media\Media entity.');
}
$deleteForm = $this->createDeleteForm($id);
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
$em->flush();
return $this->redirect($this->generateUrl('img_edit', array('id' => $id)));
}
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
/**
* Deletes a media\Media entity.
*
* #Route("/{id}", name="img_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, $id)
{
$form = $this->createDeleteForm($id);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('testTestBundle:media\Media')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find media\Media entity.');
}
$em->remove($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('img'));
}
/**
* Creates a form to delete a media\Media entity by id.
*
* #param mixed $id The entity id
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm($id)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('img_delete', array('id' => $id)))
->setMethod('DELETE')
->add('submit', 'submit', array('label' => 'Delete'))
->getForm()
;
}
}
Form
namespace test\TestBundle\Form\media;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class MediaType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file','file', array('required' => false))
->add('name')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'test\TestBundle\Entity\media\Media'
));
}
/**
* #return string
*/
public function getName()
{
return 'test_testbundle_media_media';
}
}
I you are playing with Symfony2 forms and file upload I'd not recommend doing it as it's described in official documentation.
Please take a look at https://github.com/dustin10/VichUploaderBundle, configure your upload, link entity and enjoy!
I think the documentation is a great example for basic file upload.
Try to update your entity like this :
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="name",type="string",length=255)
* #Assert\NotBlank
*/
public $name;
/**
* #ORM\Column(name="path",type="string",length=255, nullable=true)
*/
public $path;
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return File
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set path
*
* #param string $path
* #return File
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
public function getWebPath()
{
return null === $this->path ? null : $this->getUploadDir().'/'.$this->path;
}
public function getUploadRootDir()
{
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
public function getUploadDir()
{
return 'uploads/documents/';
}
public function upload()
{
if (null === $this->getFile()){
return;
}
$this->getFile()->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
$this->path = $this->getFile()->getClientOriginalName();
$this->file = null;
}
And in your controller, a simple upload action for GET and POST :
public function UploadAction()
{
$document = new Media();
$form = $this->createFormBuilder($document)
->add('file')
->getForm();
if ($this->getRequest()->isMethod('POST')) {
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$account = $em->getRepository('AccountingAccountBundle:Account')->findOneBy(array('id' => $accountId));
$document->setAccount($account);
$document->upload();
$em->persist($document);
$em->flush();
}
}
return array('form' => $form->createView());
}
If this example don't work on your server, maybe you have a problem with rights in your file system.
the problem was in the path directories, just needed one more directory return dir.'/../../../../../web/uploads'; like this :)
Most simplefied idea:
Create normal form in twig,and try to use/rebuilt to your own need trait below.
I used it in some simple uploads connected to entity, it's not a great code (no file validation etc. ) , but it can be a good start for you.
$location variable is configured if trait is in entity dir
user trait below to upload (
<?php
namespace AdminBundle\Entity;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
trait LinkFileTrait
{
private $location='/../../../web/media/link' ;
private $path=__DIR__;
public function checkDir(){
$id=$this->getId();
$fs=new Filesystem();
if(!$fs->exists($this->path.$this->location))
$fs->mkdir($this->path.$this->location);
$this->setImagePath($this->path.$this->location.'/'.$id.'');
if(!$fs->exists($this->getImagePath()))
$fs->mkdir($this->getImagePath());
}
public function getImages(){
$this->checkDir();
$finder = new Finder();
return $finder->files() ->in($this->getImagePath());
}
public function getRandomImage(){
$images=$this->getImages();
$images=iterator_to_array($images);
if(count($images)<1)
return null;
shuffle($images);
return $images[0];
}
public function UploadFile(UploadedFile $file){
$file->move($this->getImagePath().'/',$file->getClientOriginalName());
}
public function removeImage($file){
$this->checkDir();
$fs=new Filesystem();
if($fs->exists($this->getImagePath().'/'.$file))
$fs->remove($this->getImagePath().'/'.$file);
}
public function imageExists($image){
$this->checkDir();
$fs=new Filesystem();
return $fs->exists($this->getImagePath().'/'.$image);
}
public function getRelativePath($path){
$path=strtr($path,"\\","/");
$x=explode('../web/',$path);
return $x[1];
}
public function getImageName($path){
$path=strtr($path,"\\","/");
$x=explode('/',$path);
return $x[count($x)-1];
}
public function getFileName($path){
$x=explode('/',$path);
return $x[count($x)-1];
}
}
and in your controller
public function imagesAction(Link $link,Request $request)
{
$link->checkDir();
$images=$link->getImages();
if($request->getMethod()=='POST'){
$file=$request->files->all()['plik'];
$link->UploadFile($file);
}
return array('link'=>$link,'images'=>$images);
}

Symfony2 dependent dropdown ManyToMany

I have 3 entities: Company, Industry, Category
I would like to create a form where the user can input the name of the company and then selects the Industry from a dropdown list. Every Industry has Categories. When user selects a Industry I want to populate the Category list. I've read following article: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data
I've created the form but when the ajax call is triggered I get following error:
Neither the property "categories" nor one of the methods "setCategories()", "__set()" or "__call()" exist and have public access in class "ebulucu\MainBundle\Entity\Company"
I'm on this now many days and just can't get it work. I hope somebody have some hints for me. I need a form with one input field for the company name, two dropdowns Industry and Category where Category depends on the selected Industry. Company has a ManyToMany relation to Category and Industry has a OneToMany relation too category. So far this is my code:
Edit:
I have tried the code with OneToMany instead ManyToMany relation between Company and Category.
That works fine. But what to do in case ManyToMany Relation? How to manage to load and set Categories?
my 3 entities:
class Company
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToMany(targetEntity="Category", mappedBy="companies")
*/
private $categories;
/**
* Constructor
*/
public function __construct()
{
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
... setters and getters
class Industry
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=80)
* #Assert\NotBlank()
*/
private $name;
/**
* #var
*
* #ORM\OneToMany(targetEntity="Category",mappedBy="industry")
*/
private $categories;
public function __construct()
{
$this->categories = new ArrayCollection();
}
...setters and getters
class Category
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=80)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="Industry", inversedBy="categories");
* #ORM\JoinColumn(name="industry_id", referencedColumnName="id",nullable=false)
*/
private $industry;
/**
* #ORM\ManyToMany(targetEntity="Company", inversedBy="categories");
* #ORM\JoinTable(name="categories_companies")
*/
private $companies;
... setters and getters
My Company Form Class:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use ebulucu\MainBundle\Entity\Industry;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Doctrine\ORM\EntityRepository;
class CompanyRegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('industry', 'entity', array(
'mapped' => false,
'class' => 'ebulucuMainBundle:Industry',
'property' => 'name',
'empty_value' => 'Choose industry',
));
$formModifier = function(FormInterface $form, $industry_id) {
if($industry_id) {
$form->add('categories', 'entity', array(
'class' => 'ebulucuMainBundle:Category',
'query_builder' => function(EntityRepository $er) use ($industry_id) {
$query = $er->createQueryBuilder('i')
->select(array('i'))
->where('i.industry_id = :industry_id')
->setParameter('industry_id', $industry_id)
->orderBy('i.name', 'ASC');
return $query;
},
'empty_value' => 'Choose category'
)
);
}
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use ($formModifier) {
$formModifier($event->getForm(), null);
}
);
//** Checks for Industry that is submitted and adds categories based on industry selection **//
$builder->get('industry')->addEventListener(
FormEvents::POST_SUBMIT, function(FormEvent $event) use ($formModifier) {
$industry_id = $event->getData();
$formModifier($event->getForm()->getParent(), $industry_id);
}
);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'ebulucu\MainBundle\Entity\Company',
));
}
public function getName()
{
return 'company';
}
}
The Controller:
use ebulucu\MainBundle\Entity\Company;
use ebulucu\MainBundle\Form\Type\CompanyRegistrationFormType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use ebulucu\MainBundle\Entity\Industry;
use ebulucu\MainBundle\Entity\Category;
use Symfony\Component\HttpFoundation\Request;
class MainController extends Controller
{
/**
* #Route("/", name="homepage")
* #Template()
*/
public function indexAction(Request $request)
{
$company = new Company();
$form = $this->createForm(new CompanyRegistrationFormType(), $company);
$form->handleRequest($request);
if ($form->isValid()) {
return $this->redirect($this->generateUrl('fos_user_security_login'));
}
return array(
'form' => $form->createView(),
);
}
/**
* #Route("/", name="loadIndustryCategories")
* #Template()
*/
public function loadIndustryCategories(Request $request)
{
$company = new Company();
$form = $this->createForm(new CompanyRegistrationFormType(), $company);
$form->handleRequest($request);
return array(
'form' => $form->createView(),
);
}
}
Twig Template with form and ajax call:
{% block content %}
<div>Homepage</div>
{{ form_start(form, {'attr': {'id': 'form_industry'}}) }}
{{ form_end(form) }}
{% endblock %}
{% block js%}
<script>
$('#company_industry').change( function() {
var postData = $("#form_industry").serializeArray();
var formURL = {{ path('loadIndustryCategories') }};
$.ajax(
{
url : formURL,
type: "POST",
data : postData,
success:function(data, textStatus, jqXHR)
{
//data: return data from server
},
error: function(jqXHR, textStatus, errorThrown)
{
//if fails
}
});
e.preventDefault(); //STOP default action
e.unbind(); //unbind. to stop multiple form submit.
});
</script>
{% endblock %}
Company:categories will be a collection of entities, not a singular entity, so it shouldn't have a setCategory method, otherwise you can inadvertently remove your whole collection. Instead you will have an addCategories, removeCategories and getCategories (this could be addCategorie and removeCategorie if doctrine generated due to its 'un-pluralization').
To fix, you need to change your categories form element to a type of collection instead of an entity in CompanyRegistrationFormType.
Just checking, should a company have one category or multiple? At the moment, your code seems to be somewhat a bit of both?

Symfony2 FOSRestBundle timestamp validation fails

I'm trying to send a post request including two timestamps to my REST api.
The problem is that the timestamps are marked as invalid. "This value is not valid."
What am I doing wrong?
This is the request:
POST http://localhost:8000/app_test.php/api/projects/1/tasks/1/timetrackings
Accept: application/json
Content-Type: application/json
{"timeStart":1390757625,"timeEnd":1390757625,"projectMember":1}
The Controller looks as follows:
class MemberController extends BaseApiController implements ClassResourceInterface
{
public function postAction($projectId, $taskId, Request $request)
{
/** #var EntityManager $em */
$em = $this->getDoctrine()->getManager();
$this->findProject($em, $projectId);
$task = $this->findTask($em, $projectId, $taskId);
$request->request->add(array(
'task' => $taskId,
));
$form = $this->createForm(new TimeTrackType(), new TimeTrack());
$form->submit($request->request->all());
if ($form->isValid())
{
/** #var TimeTrack $tracking */
$tracking = $form->getData();
$task->addTimeTrack($tracking);
$em->flush();
return $this->redirectView(
$this->generateUrl('api_get_project_task_timetracking', array(
'projectId' => $projectId,
'taskId' => $taskId,
'trackingId' => $tracking->getId(),
)),
Codes::HTTP_CREATED
);
}
return View::create($form, Codes::HTTP_BAD_REQUEST);
}
}
The TimeTrackType class:
namespace PMTool\ApiBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TimeTrackType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('timeStart', 'datetime', array(
'input' => 'timestamp',
))
->add('timeEnd', 'datetime', array(
'input' => 'timestamp',
))
->add('projectMember')
->add('task')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'PMTool\ApiBundle\Entity\TimeTrack',
'csrf_protection' => false,
));
}
/**
* #return string
*/
public function getName()
{
return 'timetrack';
}
}
The entity class:
namespace PMTool\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use \DateTime;
/**
* TimeTrack
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="PMTool\ApiBundle\Entity\TimeTrackRepository")
*/
class TimeTrack
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var DateTime
*
* #ORM\Column(name="timeStart", type="datetime")
*/
private $timeStart;
/**
* #var DateTime
*
* #ORM\Column(name="timeEnd", type="datetime")
*/
private $timeEnd;
/**
* #var ProjectMember
*
* #ORM\ManyToOne(targetEntity="ProjectMember")
*/
private $projectMember;
/**
* #var Task
*
* #ORM\ManyToOne(targetEntity="Task", inversedBy="timeTracks")
* #ORM\JoinColumn(name="taskId", referencedColumnName="id")
*/
private $task;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set timeStart
*
* #param DateTime $timeStart
* #return TimeTrack
*/
public function setTimeStart($timeStart)
{
$this->timeStart = $timeStart;
return $this;
}
/**
* Get timeStart
*
* #return DateTime
*/
public function getTimeStart()
{
return $this->timeStart;
}
/**
* Set timeEnd
*
* #param DateTime $timeEnd
* #return TimeTrack
*/
public function setTimeEnd($timeEnd)
{
$this->timeEnd = $timeEnd;
return $this;
}
/**
* Get timeEnd
*
* #return DateTime
*/
public function getTimeEnd()
{
return $this->timeEnd;
}
/**
* #return \PMTool\ApiBundle\Entity\Task
*/
public function getTask()
{
return $this->task;
}
/**
* #param \PMTool\ApiBundle\Entity\Task $task
* #return $this
*/
public function setTask($task)
{
$this->task = $task;
return $this;
}
/**
* #return \PMTool\ApiBundle\Entity\ProjectMember
*/
public function getProjectMember()
{
return $this->projectMember;
}
/**
* #param \PMTool\ApiBundle\Entity\ProjectMember $projectMember
* #return $this
*/
public function setProjectMember($projectMember)
{
$this->projectMember = $projectMember;
return $this;
}
}
You can use a transformer to achieve this. (See Symfony Transformers)
Here is an Example of my FormType:
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
...
$transformer = new DateTimeToTimestampTransformer();
$builder->add($builder->create('validFrom', 'text')
->addModelTransformer($transformer)
)
Be sure to use "'text'" as an input type, otherwise it didn't work for me.
the input option is just for the model side of your underlying data object. In your case it should be datetime.
Your problem is, that you want to transform the timestamp into view data that the symfony form datetime form type does recognise. And I dont know how to do that, unfortunately.
http://symfony.com/doc/current/reference/forms/types/datetime.html#input

Resources