I'm stuck at a problem with ZendFramework 2, annotationbuilder and fileupload.
for at contact form i want user the choice to upload a file.
I got everything to work except the file upload, if there is no file a get the
error:
File was not uploaded
I'm using annotationbuilder to create the form. some annotations is cut out with a space for testing. but did not help.
the Annotation class:
<?php
/**
* #Annotation\Name("message")
* #Annotation\Hydrator("Zend\Stdlib\Hydrator\ObjectProperty")
* #ORM\Entity
* #ORM\Table(name="contact_message")
*/
class Message {
/**
* #Annotation\Exclude()
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #var integer
*/
private $id;
/**
* #Annotation\Type("Zend\Form\Element\Select")
* #Annotation\Flags({"priority": 600})
* #Annotation\Required({"required":"true" })
* #Annotation\Filter({"name":"StringTrim"})
* # Annotation\Validator({"name":"StringLength"})
* #Annotation\Options({"label":"About:"})
* #Annotation\Attributes({"options":{"1":"PlaceHolder","2":"Test"}})
*
* #ORM\Column(type="string")
* #var String
*/
private $about;
/**
* #Annotation\Type("Zend\Form\Element\Text")
* #Annotation\Flags({"priority": 500})
* #Annotation\Required({"required":"true" })
* #Annotation\Filter({"name":"StripTags"})
* # Annotation\Validator({"name":"EmailAddress"})
* #Annotation\Options({"label":"Name:"})
* #Annotation\Attributes({"required": true,"placeholder": "Your name ... "})
*
* #ORM\Column(type="string")
* #var String
*/
private $name;
/**
* #Annotation\Type("Zend\Form\Element\Text")
* #Annotation\Flags({"priority": 500})
* #Annotation\Required({"required":"true" })
* #Annotation\Filter({"name":"StripTags"})
* # Annotation\Validator({"name":"EmailAddress"})
* #Annotation\Options({"label":"Subject:"})
* #Annotation\Attributes({"required": true,"placeholder": "Subject ... "})
*
* #ORM\Column(type="string")
* #var String
*/
private $subject;
/**
* #Annotation\Type("Zend\Form\Element\File")
* #Annotation\Flags({"priority": 500})
* # Annotation\Required({"required":false })
* # Annotation\Filter({"name":"StringTrim","filerenameupload":{"target": "./img","randomize":true}})
* # Annotation\Filter({"name":"StripTags"})
* # Annotation\Validator({"name":"StringLength"})
* #Annotation\Options({"label":"File:"})
* #Annotation\Attributes({"required": false})
*
* #ORM\Column(type="string")
* #var String
*/
private $file;
/**
* #Annotation\Type("Zend\Form\Element\Textarea")
* #Annotation\Flags({"priority": 500})
* # Annotation\Required({"required":"true" })
* #Annotation\Filter({"name":"StripTags"})
* # Annotation\Validator({"name":"EmailAddress"})
* #Annotation\Options({"label":"Message:"})
* #Annotation\Attributes({"required": true,"placeholder": "Message ... "})
*
* #ORM\Column(type="string")
* #var String
*/
private $message;
/**
* WARNING USING THESE IS NOT SAFE. there is no checking on the data and you need to know what
* you are doing when using these.
* But it a great function for lazy people ;)
*
* #param ANY $value
* #param ANY $key
* #return $value
*/
public function __set($value,$key){
return $this->$key = $value;
}
/**
* WARNING USING THESE IS NOT SAFE. there is no checking on the data and you need to know what
* you are doing when using these.
* But it a great function for lazy people ;)
*
* #param ANY $value
* #param ANY $key
* #return $value
*/
public function __get($key){
return $this->$key;
}
/**
* WARNING USING THESE IS NOT SAFE. there is no checking on the data and you need to know what
* you are doing when using these.
* This is used to exchange data from form and more when need to store data in the database.
* and again ist made lazy, by using foreach without data checks
*
* #param ANY $value
* #param ANY $key
* #return $value
*/
public function populate($array){
foreach ($array as $key => $var){
$this->$key = $var;
}
}
/**
* Get an array copy of object
*
* #return array
*/
public function getArrayCopy()
{
return get_object_vars($this);
}
}
?>
and the add Action in my controller:
<?php
use Zend\View\Model\ViewModel;
use Zend\Form\Annotation\AnnotationBuilder;
use Contact\Entity\Contact;
use Contact\Entity\Company;
use Contact\Entity\Message;
use Contact\Controller\EntityUsingController;
class MessageController extends EntityUsingController {
public function addAction(){
$message = new Message();
$builder = new AnnotationBuilder();
$form = $builder->createForm($message);
$form->bind($message);
$request = $this->getRequest();
if ($request->isPost()) {
$form->bind($message);
$requestData = array_merge_recursive((array) $request->getPost(),(array) $request->getFiles());
$form->setData($requestData);
var_dump($request->getFiles());
if ($form->isValid()) {
$em = $this->getEntityManager();
$em->persist($message);
$em->flush();
$this->flashMessenger()->addMessage('Contact Saved');
return $this->redirect()->toRoute('contact');
}
}
return new ViewModel(array(
'form' => $form
));
}
private function storeFile($file){
if (!$this->getConfiguration('fileupload')){
return null;
}
$fileBank = $this->getServiceLocator()->get('FileRepository');
$entity = $fileBank->save('/tmp/myfile.jpg');
}
}
?>
I hope anyone can help my get around this problem.
This seems to be a bug in the annotation builder.
I found a lot of places on the net that confirmed that i should be correct.
so i ended up hacking it by adding the file input after the form was build.
if ($this->getConfiguration('fileupload')){
$companyForm->add(array(
'name' => 'file',
'priority' => 300,
'type' => 'file',
'options' => array(
'label' => 'File:',
),
),array('priority' => 300));
}
checks if the configuration allows for file upload. an extra feature I put in now that I could not set the file upload as an annotation
If you're still interested in another way:
You can try the AllowEmpty annotation:
* #Annotation\AllowEmpty(true)
Or you can try to disable the validator for that field in the controller:
$form->getInputFilter()->remove('file');
Related
/*When I try to view the output of my project on the browser, I am redirected to the following error
"ErrorException (E_WARNING)rtrim () expects the parameter 1 to be
string, given the object ".
Can someone help me?
*/
i find the problem in following function public function setBasePath($basePath ){
below the code Application.php:
<?php
namespace Illuminate\Foundation;
use Closure;
use RuntimeException;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Container\Container;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Log\LogServiceProvider;
use Illuminate\Support\ServiceProvider;
use Illuminate\Events\EventServiceProvider;
use Illuminate\Routing\RoutingServiceProvider;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Contracts\Http\Kernel as HttpKernelContract;
use Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables;
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
class Application extends Container implements ApplicationContract, HttpKernelInterface
{
/**
* The Laravel framework version.
*
* #var string
*/
const VERSION = '5.6.39';
/**
* The base path for the Laravel installation.
*
* #var string
*/
protected $basePath;
/**
* Indicates if the application has been bootstrapped before.
*
* #var bool
*/
protected $hasBeenBootstrapped = false;
/**
* Indicates if the application has "booted".
*
* #var bool
*/
protected $booted = false;
/**
* The array of booting callbacks.
*
* #var array
*/
protected $bootingCallbacks = [];
/**
* The array of booted callbacks.
*
* #var array
*/
protected $bootedCallbacks = [];
/**
* The array of terminating callbacks.
*
* #var array
*/
protected $terminatingCallbacks = [];
/**
* All of the registered service providers.
*
* #var array
*/
protected $serviceProviders = [];
/**
* The names of the loaded service providers.
*
* #var array
*/
protected $loadedProviders = [];
/**
* The deferred services and their providers.
*
* #var array
*/
protected $deferredServices = [];
/**
* The custom database path defined by the developer.
*
* #var string
*/
protected $databasePath;
/**
* The custom storage path defined by the developer.
*
* #var string
*/
protected $storagePath;
/**
* The custom environment path defined by the developer.
*
* #var string
*/
protected $environmentPath;
/**
* The environment file to load during bootstrapping.
*
* #var string
*/
protected $environmentFile = '.env';
/**
* The application namespace.
*
* #var string
*/
protected $namespace;
/**
* Create a new Illuminate application instance.
*
* #param string|null $basePath
* #return void
*/
public function __construct($basePath = null)
{
if ($basePath) {
$this->setBasePath($basePath);
}
$this->registerBaseBindings();
$this->registerBaseServiceProviders();
$this->registerCoreContainerAliases();
}
/**
* Get the version number of the application.
*
* #return string
*/
public function version()
{
return static::VERSION;
}
/**
* Register the basic bindings into the container.
*
* #return void
*/
protected function registerBaseBindings()
{
static::setInstance($this);
$this->instance('app', $this);
$this->instance(Container::class, $this);
$this->instance(PackageManifest::class, new PackageManifest(
new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
));
}
/**
* Register all of the base service providers.
*
* #return void
*/
protected function registerBaseServiceProviders()
{
$this->register(new EventServiceProvider($this));
$this->register(new LogServiceProvider($this));
$this->register(new RoutingServiceProvider($this));
}
/**
* Run the given array of bootstrap classes.
*
* #param array $bootstrappers
* #return void
*/
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
$this->make($bootstrapper)->bootstrap($this);
$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
}
}
/**
* Register a callback to run after loading the environment.
*
* #param \Closure $callback
* #return void
*/
public function afterLoadingEnvironment(Closure $callback)
{
return $this->afterBootstrapping(
LoadEnvironmentVariables::class, $callback
);
}
/**
* Register a callback to run before a bootstrapper.
*
* #param string $bootstrapper
* #param \Closure $callback
* #return void
*/
public function beforeBootstrapping($bootstrapper, Closure $callback)
{
$this['events']->listen('bootstrapping: '.$bootstrapper, $callback);
}
/**
* Register a callback to run after a bootstrapper.
*
* #param string $bootstrapper
* #param \Closure $callback
* #return void
*/
public function afterBootstrapping($bootstrapper, Closure $callback)
{
$this['events']->listen('bootstrapped: '.$bootstrapper, $callback);
}
/**
* Determine if the application has been bootstrapped before.
*
* #return bool
*/
public function hasBeenBootstrapped()
{
return $this->hasBeenBootstrapped;
}
/**
* Set the base path for the application.
*
* #param string $basePath
* #return $this
*/
public function setBasePath($basePath )
{
$this->basePath = rtrim($basePath, '\/');
$this->bindPathsInContainer();
return $this;
}
/**
* Bind all of the application paths in the container.
*
* #return void
*/
protected function bindPathsInContainer()
{
$this->instance('path', $this->path());
$this->instance('path.base', $this->basePath());
$this->instance('path.lang', $this->langPath());
$this->instance('path.config', $this->configPath());
$this->instance('path.public', $this->publicPath());
$this->instance('path.storage', $this->storagePath());
$this->instance('path.database', $this->databasePath());
$this->instance('path.resources', $this->resourcePath());
$this->instance('path.bootstrap', $this->bootstrapPath());
}
/**
* Get the path to the application "app" directory.
*
* #param string $path Optionally, a path to append to the app path
* #return string
*/
public function path($path = '')
{
return $this->basePath.DIRECTORY_SEPARATOR.'app'.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
/**
* Get the base path of the Laravel installation.
*
* #param string $path Optionally, a path to append to the base path
* #return string
*/
public function basePath($path = '')
{
return $this->basePath.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
/**
* Get the path to the bootstrap directory.
*
* #param string $path Optionally, a path to append to the bootstrap path
* #return string
*/
public function bootstrapPath($path = '')
{
return $this->basePath.DIRECTORY_SEPARATOR.'bootstrap'.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
/**
* Get the path to the application configuration files.
*
* #param string $path Optionally, a path to append to the config path
* #return string
*/
public function configPath($path = '')
{
return $this->basePath.DIRECTORY_SEPARATOR.'config'.($path ? DIRECTORY_SEPARATOR.$path : $path);
}
/**
* Get the path to the database directory.
*
* #param string $path Optionally, a path to append to the database path
* #return string
*/
public function databasePath($path = '')
{
return ($this->databasePath ?: $thi`enter code here`s->basePath.DIRECTORY_SEPARATOR.'database').($path ? DIRECTORY_SEPARATOR.$path : $path);
}
Can you check you .env for APP_BASE_PATH?
The Application is initialized in bootstrap/app.php file
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
As you can see it looks APP_BASE_PATH environment variable and if this is not set it uses the path to your root application folder.
dirname(__DIR__) returns the path to the parent folder.
Based on the details you posted it looks like the problem can be with the value you set for APP_BASE_PATH
This response assumes you are using the latest version of Laravel, 5.8
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.
Hi guys i have implemented vichuploadbundle to upload images but i want in the admin the user to remove the image.
<?php
namespace George\PageBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use George\UserBundle\Entity\User;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* Page
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="George\PageBundle\Entity\PageRepository")
* #Vich\Uploadable
*/
class Page
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="text")
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="content", type="text")
*/
private $content;
/**
* #var boolean
*
* #ORM\Column(name="visible", type="boolean")
*/
private $visible;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
/**
* #var \DateTime
*
* #ORM\Column(name="modefied", type="datetime")
*/
private $modefied;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255)
*/
private $description;
/**
* #var string
*
* #ORM\Column(name="keywords", type="string", length=255)
*/
private $keywords;
/**
* #ORM\ManyToOne(targetEntity="George\UserBundle\Entity\User", inversedBy="pages")
* #ORM\JoinColumn(onDelete="CASCADE")
*/
private $owner;
//#ORM\Column(length=128, unique=true)
/**
* #Gedmo\Slug(fields={"title"})
* #ORM\Column(length=128, unique=true)
*/
private $slug;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Page
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*
* #return Page
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set visible
*
* #param boolean $visible
*
* #return Page
*/
public function setVisible($visible)
{
$this->visible = $visible;
return $this;
}
/**
* Get visible
*
* #return boolean
*/
public function getVisible()
{
return $this->visible;
}
/**
* Set created
*
* #param \DateTime $created
*
* #return Page
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set modefied
*
* #param \DateTime $modefied
*
* #return Page
*/
public function setModefied($modefied)
{
$this->modefied = $modefied;
return $this;
}
/**
* Get modefied
*
* #return \DateTime
*/
public function getModefied()
{
return $this->modefied;
}
/**
* Set description
*
* #param string $description
*
* #return Page
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set keywords
*
* #param string $keywords
*
* #return Page
*/
public function setKeywords($keywords)
{
$this->keywords = $keywords;
return $this;
}
/**
* Get keywords
*
* #return string
*/
public function getKeywords()
{
return $this->keywords;
}
/**
* #return mixed
*/
public function getOwner()
{
return $this->owner;
}
/**
* #param mixed $owner
*/
public function setOwner(User $owner)
{
$this->owner = $owner;
}
public function getSlug()
{
return $this->slug;
}
// ..... other fields
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="product_image", fileNameProperty="imageName")
*
* #var File
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $imageName;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
private $updatedAt;
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTime('now');
}
}
/**
* #return File
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* #param string $imageName
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
}
/**
* #return string
*/
public function getImageName()
{
return $this->imageName;
}
}
Can you advice me what is the best solution to this. I think it will be best if i have a method remove image and in the method try to use bundles build in function for removal the image (which i don't find anywhere) and update it in the database. In the front end this can be don trough ajax or on submit... Is there a build in function for the removal of file in the vichuploadbundle?
I have found how to remove the image. The bundle provides a custom form type in order to ease the upload, deletion and download of images.
When you build the form you specifies the widget:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->add('image', 'vich_image', array(
'required' => false,
'allow_delete' => true, // not mandatory, default is true
'download_link' => true, // not mandatory, default is true
));
}
This add a twig template for deleting the img :)
I am trying to handle image uploads via a form and then display these image uploads elsewhere in my website.
So far I have been looking at the Cookbook and also at other tutorials.I have created an Entity and a Form and am able to submit , but I am not sure if I am storing the file itself in my DB or just the name of it.
When I try to display the images in my view resource not found error on the images. Any suggestions on the correct way to upload images in Symfony?
and this is what I have so far. Entity
<?php
namespace BlogBundle\Entity;
use Symfony\Component\HttpFoundation\File\File;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Photo
*
*
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Upload
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*Image File
* #var File
* #Assert\File( maxSize = "5M",mimeTypes = {"image/jpeg", "image/gif", "image/png", "image/tiff"}, mimeTypesMessage = "Please upload a valid Image")
*
*/
private $file;
/**
* #ORM\Column(type="string", length=500)
*/
private $title;
/**
* #ORM\Column(type="string", length=500)
*/
private $description;
/**
* Image path
*
* #var string
*
* #ORM\Column(type="text", length=255, nullable=false)
*/
protected $path;
protected function getUploadRootDir()
{
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw up
// when displaying uploaded doc/image in the view.
return 'uploads/documents';
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return Upload
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set description
*
* #param string $description
* #return Upload
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set path
*
* #param string $path
* #return Upload
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
/**
* Called before saving the entity
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
// do whatever you want to generate a unique name
$filename = sha1(uniqid(mt_rand(), true));
$this->path = $filename.'.'.$this->file->guessExtension();
}
}
/**
* Called before entity removal
*
* #ORM\PreRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath())
{
unlink($file);
}
}
/**
* Called after entity persistence
*
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
// The file property can be empty if the field is not required
if (null === $this->file) {
return;
}
// Use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->file->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
$this->path = $this->getFile()->getClientOriginalName();
// Clean up the file property as you won't need it anymore
$this->file = null;
}
/**
* Sets file.
*
* #param UploadedFile $file
*#return Upload
*/
public function setFile(File $file = null)
{
$this->file = $file;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
}
Controller methods
/** This is the homepage for the admin area, for adding,deleting,editing of blog posts.
* #Route("/posted/admin/upload", name="upload")
* #Template()
*/
public function uploadimageAction(Request $request)
{
$upload = new Upload();
//create checkboxtype form
$form = $this->createForm(new ImageFormType(), $upload, array(
'action' => $this->generateUrl('upload'),
'method' => 'POST',
));
$form->handleRequest($request);
if($form->isValid()){
$em = $this->getDoctrine()->getManager();
$upload->upload();
$em->persist($upload);
$em->flush();
// exit(\Doctrine\Common\Util\Debug::dump($post));
return $this->render('BlogBundle:Default:success.html.twig'
);
;
}
return $this->render('BlogBundle:Default:upload.html.twig',array(
'form' =>$form->createView(),
));
}
You are only storing the path in the DB, and only it's necesary that.
Using path you can show the file in your view
pd: you are storing the file in your server with:
$this->file->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
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