Symfony - API Platform - File Upload - symfony

I'm trying to implement a file upload with API PLatform following the Documentation using Vich. But it's not working, precisely, the MediaObject is not hydrated by the file I send on my request.
I followed quite exactly the cookbook provided by API Platform but it doesn't seem that my form handle the request well, because it doesn't pass the constraint validation and I get this answer from the API:
{
"type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10",
"title": "An error occurred",
"detail": "file: This value should not be null.",
"violations": [
{
"propertyPath": "file",
"message": "This value should not be null."
}
]
}
My request is basic : header is multipart/form-data and I send a single and small file (78Ko):
"file" => image.jpg
When I dump my $request object, the file is in it, but the $form->getData() is not hydrated. Any idea why ?
Here is my MediaObject :
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Traits\updatedAt;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Entity\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ApiResource(iri="http://schema.org/MediaObject", collectionOperations={
* "get",
* "post"={
* "method"="POST",
* "path"="/media_objects",
* "controller"=CreateMediaObjectAction::class,
* "defaults"={"_api_receive"=false},
* },
* })
* #Vich\Uploadable
* #ORM\Entity(repositoryClass="App\Repository\MediaObjectRepository")
* #ORM\HasLifecycleCallbacks()
*/
class MediaObject
{
use updatedAt;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var File|null
* #Assert\NotNull()
* #Vich\UploadableField(mapping="media_object", fileNameProperty="contentUrl")
*/
public $file;
/**
* #var string|null
* #ORM\Column(type="string", length=255, nullable=true)
* #ApiProperty(iri="http://schema.org/contentUrl")
*/
private $contentUrl;
public function getId(): ?int
{
return $this->id;
}
public function getFile(): ?string
{
return $this->file;
}
/**
* #param string $file
*
* #return MediaObject
* #throws \Exception
*/
public function setFile(string $file): self
{
$this->file = $file;
if (null !== $file) {
$this->updatedAt = new DateTimeImmutable();
}
return $this;
}
public function getContentUrl(): ?string
{
return $this->contentUrl;
}
public function setContentUrl(?string $contentUrl): self
{
$this->contentUrl = $contentUrl;
return $this;
}
}
Here is my Media ObjectType :
namespace App\Form;
use App\Entity\MediaObject;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;
class MediaObjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// Configure each fields you want to be submitted here, like a classic form.
->add('file', VichFileType::class, [
'label' => 'label.file',
'required' => false,
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => MediaObject::class,
'csrf_protection' => false,
]);
}
public function getBlockPrefix()
{
return '';
}
}
And here is my controller :
namespace App\Controller;
use ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException;
use App\Entity\MediaObject;
use App\Form\MediaObjectType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;
final class CreateMediaObjectController extends AbstractController
{
private $validator;
private $doctrine;
private $factory;
public function __construct(RegistryInterface $doctrine, FormFactoryInterface $factory, ValidatorInterface $validator)
{
$this->validator = $validator;
$this->doctrine = $doctrine;
$this->factory = $factory;
}
/**
* #param Request $request
* #Route("media_objects", name="media")
*
* #return MediaObject
*/
public function mediaCreation(Request $request): MediaObject
{
$mediaObject = new MediaObject();
$form = $this->factory->create(MediaObjectType::class, $mediaObject);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->doctrine->getManager();
$em->persist($mediaObject);
$em->flush();
// Prevent the serialization of the file property
$mediaObject->file = null;
return $mediaObject;
}
// This will be handled by API Platform and returns a validation error.
throw new ValidationException($this->validator->validate($mediaObject));
}
}

I am using postman and symfony 4.x for this answer.
API URL: localhost/api/media_objects (localhost => baseURL)
Select form-data => Key should be file and select type File ( If
you hover to right side to input you will see drop down)
Choose file and post.
NOTE: you must have config/packages/vich_uploader.yaml with configurations like
vich_uploader:
db_driver: orm
mappings:
media_object:
uri_prefix: /media
upload_destination: '%kernel.project_dir%/public/media'
namer: Vich\UploaderBundle\Naming\UniqidNamer

Related

symfony / doctrine deliveres partially incorrect data - using uuids

I have a big problem with Symfony, Doctrine and partially incorrect data.
First of all
i use uuids for primary keys
i use jwts for authentication
i use symfony 5.4.x and doctrine-bundle 2.7.x
i use class inheriance for getting my pk on each entity
i donĀ“t use at all relations, because i want to have some flexibility to slice components into their origin e.g. security component as a microservice (but i think this is not really neccesarry on my question)
Sample of JWT content
{
"iat": xxx,
"exp": xxx,
"roles": [
"MY_ROLE"
],
"id": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE",
"tenant": {
"id": "11111111-2222-3333-4444-555555555555",
"name": "MyTenant"
},
"user": {
"id": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE",
"username": "MyUser",
"firstname": "My",
"name": "User"
}
}
Sample of Entity: AbstractEntity.php
<?php
namespace App\Entity;
use JsonSerializable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
/**
* Class AbstractEntity
* #package App\Entity
*
* #ORM\MappedSuperclass()
*/
abstract class AbstractEntity implements EntityInterface, JsonSerializable
{
/**
* #ORM\Id
* #ORM\Column(type="uuid", unique=true)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class=UuidGenerator::class)
*/
private ?Uuid $id = null;
/**
* #return Uuid|null
*/
public function getId(): ?Uuid
{
return $this->id;
}
/**
* #return bool
*/
public function isNew(): bool
{
return !isset($this->id);
}
/**
* #return array
*/
public function jsonSerialize(): array
{
return [
'id' => $this->getId()->toRfc4122()
];
}
/**
* #return Uuid
*/
public function __toString(): string
{
return $this->getId()->toRfc4122();
}
}
Sample of Entity: Settings.php
<?php
namespace App\Entity\Configuration;
use DateTime;
use App\Entity\AbstractEntity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Uid\Uuid;
/**
* #ORM\Entity(repositoryClass="App\Repository\Configuration\SettingsRepository")
* #ORM\Table(name="configuration_settings", indexes={#ORM\Index(columns={"tenant"})})
*/
class Settings extends AbstractEntity
{
/**
* #var Uuid $tenant
*
* #ORM\Column(type="uuid")
*/
private Uuid $tenant;
/**
* #var string|null $payload
*
* #ORM\Column(type="json", nullable=true)
*/
private ?string $payload = "";
/**
* #return Uuid
*/
public function getTenant(): Uuid
{
return $this->tenant;
}
/**
* #param Uuid $tenant
*/
public function setTenant(Uuid $tenant): void
{
$this->tenant = $tenant;
}
/**
* #return string|null
*/
public function getPayload(): ?string
{
return $this->payload;
}
/**
* #param string|null $payload
*/
public function setPayload(?string $payload): void
{
$this->payload = $payload;
}
}
Sample of Controller: SettingsController.php
<?php
namespace App\Controller\Configuration\API;
use App\Entity\Configuration\Settings;
use App\Entity\Security\Role;
use App\Repository\Configuration\SettingsRepository;
use App\Service\Security\UserService;
use Doctrine\ORM\EntityManagerInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Uid\Uuid;
/**
* Settings API controller.
*
* #Route("/api/configuration/settings", name="api_configuration_settings_")
*/
class SettingsController extends AbstractController
{
/**
* #var SettingsRepository
*/
private SettingsRepository $repository;
/**
* SettingsService constructor.
* #param EntityManagerInterface $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(Settings::class);
}
/**
* #Route("/obtain", name="obtain", methods={"GET"})
*/
public function obtain(UserService $userService, TokenStorageInterface $tokenStorageInterface, JWTTokenManagerInterface $jwtManager): JsonResponse
{
$userService->isGranted([
Role::ROLE_TENANT_USER
]);
$token = $jwtManager->decode($tokenStorageInterface->getToken());
$tenantAsRFC4122 = $token['tenant']['id']; // Here we have: 11111111-2222-3333-4444-555555555555
$tenant = Uuid::fromString($tenantAsRFC4122); // Here we have a class of Symfony\Component\Uid\UuidV6;
$settings = $this->repository->findOneBy([
'tenant' => $tenant
]);
return new JsonResponse(
$settings
);
}
}
If i now call the API-URL with the Authorization Header including the Bearer as JWT, i will get the Settings for my tenant 11111111-2222-3333-4444-555555555555. But this works not always ...
I can't describe it exactly, because I can't reproduce it, but on one of the systems with a lot of traffic it happens from time to time that despite the correct bearer, i.e. correct tenant, simply wrong settings for another tenant come back.
Is there a doctrine cache somewhere that might cache queries and assign them to the wrong repsonses at the end of the day?
SELECT payload FROM configuration_settings WHERE tenant = '0x1234567890123456789' // here doctrine automaticly strips it to hex
I am so perplexed, because the SQL behind it looks correct and right, here times as an example shortened.
Hope someone can help me.
Thanks,
S.

How to create an entity with several dates in a single form with EasyAdmin

I am trying to develop my first symfony web-app and I have decided to use the bundle EasyAdmin.
In this web-app, I would like to define the following model : an Event with several dates.
In order to create this, I have create 2 entities with the help of the symfony console : Event and EventDate:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\EventRepository")
*/
class Event
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\EventDate", mappedBy="event", orphanRemoval=true)
*/
private $dates;
public function __construct()
{
$this->dates = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
/**
* #return Collection|EventDate[]
*/
public function getDates(): Collection
{
return $this->dates;
}
public function addDate(EventDate $date): self
{
if (!$this->dates->contains($date)) {
$this->dates[] = $date;
$date->setEvent($this);
}
return $this;
}
public function removeDate(EventDate $date): self
{
if ($this->dates->contains($date)) {
$this->dates->removeElement($date);
// set the owning side to null (unless already changed)
if ($date->getEvent() === $this) {
$date->setEvent(null);
}
}
return $this;
}
public function __toString(): String
{
return $this->name;
}
}
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\EventDateRepository")
*/
class EventDate
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="date")
*/
private $date;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Event", inversedBy="dates")
* #ORM\JoinColumn(nullable=false)
*/
private $event;
public function getId(): ?int
{
return $this->id;
}
public function getDate(): ?\DateTimeInterface
{
return $this->date;
}
public function setDate(\DateTimeInterface $date): self
{
$this->date = $date;
return $this;
}
public function getEvent(): ?Event
{
return $this->event;
}
public function setEvent(?Event $event): self
{
$this->event = $event;
return $this;
}
}
In order to be user-friendly, I would like to "customize" the form of an Event in order to allow the user to create in the same form the event and its dates.
In order to do this, I have define the Event entity's form like that:
easy_admin:
entities:
Event:
class: App\Entity\Event
form:
fields:
- {property: 'name'}
- {property: 'dates', type: 'collection'}
The render of the collection is right because I can add or remove a date:
But As you can see, the field that represent the EventDate is an edit text. I think it's because the field represent the EventDate class and not only the date attribute.
The aim is to have the date selector that I have if I add a new EventDate in EasyAdmin:
So the question is: How to custom EasyAdmin in order to add an Event and its dates in a single form?
Thank you for your help!
I found the way to do it.
I need to modify my yaml EasyAdmin file in order to introduce an entry_type:
easy_admin:
entities:
Event:
class: App\Entity\Event
form:
fields:
- {property: 'name'}
- {property: 'dates', type: 'collection', type_options: {entry_type: 'App\Form\EventDateForm', by_reference: false}}
Then, I have to create the EventDateForm class:
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class EventDateForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('date');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'App\Entity\EventDate'
));
}
}
I also need to update the $date attribut of my Event entity like this:
/**
* #ORM\OneToMany(targetEntity="App\Entity\EventDate", mappedBy="event", orphanRemoval=true, cascade={"persist", "remove"})
*/
private $dates;
The render is not very beautiful but it works:

Sonata admin : list always use defaut language

I use sonata admin bundle with Symfony 3.4 and knplabs. Everything work fine excepted one thing.
I have create a Test class and the sonata list with the CRUD.
This class has a translatable title, when I'm on the edit mode of one on my test object, I can click on the flag for editing the title in many languages.
But when I'm on my list view, the flag are display but when I click on it, the list always display the title in english (default language).
I debug and find that in edit view, method setLocale and getLocal are used to change the languages but in list view, they are not call.
How can I translate my entites in my list view ?
Here my test class, translation test class and my testAdmin.
Test.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use Sonata\TranslationBundle\Model\TranslatableInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\TestRepository")
*/
class Test implements TranslatableInterface
{
use ORMBehaviors\Translatable\Translatable;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="boolean")
*/
private $is_enable;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->translate(null, false)->getTitle();
}
public function setTitle(string $title): self
{
$this->translate(null, false)->setTitle($title);
return $this;
}
public function getIsEnable(): ?bool
{
return $this->is_enable;
}
public function setIsEnable(bool $is_enable): self
{
$this->is_enable = $is_enable;
return $this;
}
/**
* #param string $locale
*/
public function setLocale($locale)
{
$this->setCurrentLocale($locale);
return $this;
}
/**
* #return string
*/
public function getLocale()
{
return $this->getCurrentLocale();
}
/**
* #return string
*
* Set this to have a correct name display on BO (sonata translation add some weird id key after the name)
*/
public function __toString()
{
if (empty($this->getTitle())){
return '';
}
else{
return $this->getTitle();
}
}
}
TestTranslation.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* #ORM\Table(name="test_translation")
* #ORM\Entity
*/
class TestTranslation
{
use ORMBehaviors\Translatable\Translation;
/**
* #var string
*
* #ORM\Column(type="string", length=255)
*/
private $title;
/**
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* #param string $title
*
* #return TestTranslation
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
}
TestAdmin.php
<?php
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
class TestAdmin extends AbstractAdmin
{
protected $baseRoutePattern = 'test';
protected $baseRouteName = 'test';
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('title', TextType::class)
->add('is_enable', TextType::class)
;
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('id')
->add('title')
->add('is_enable')
->add('_action', 'actions', array(
'actions' => array(
'edit' => array(),
'delete' => array(),
)
))
;
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper->add('id');
$datagridMapper->add('translations.title', null, array('label' => 'Title'));
$datagridMapper->add('is_enable');
}
}
return (string)$this->getTranslations()->get($locale);
use above code in __toString method Test.php

Symfony2: Inserting collection form data

There is a one-to-one relationship between User and Company entity.
When initializing (creating) the user's company, userID should be bind to Company user field as a foreign key. But instead of that, i get this error message:
Property "id" is not public in class
"Website\CompanyBundle\Entity\User". Maybe you should create the
method "setId()"?
Why Symfony wants to create new User when this form is about Company entity, and User entity is just a collection that should provide user's ID.
Here is my code:
Company.php entity:
namespace Website\CompanyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="Website\CompanyBundle\Entity\Repository\CompanyRepository")
* #ORM\Table(name="company")
*/
class Company
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="User", inversedBy="company")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #ORM\Column(type="string")
*/
protected $name;
}
CompanyType.php
class CompanyType extends AbstractType
{
private $security;
public function __construct(SecurityContext $security)
{
$this->security= $security;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = $this->securityContext->getToken()->getUser();
$builder
->add('user', new UserType($security))
->add('company_name')
->add('company_address')
...
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Website\CompanyBundle\Entity\Company'
));
}
public function getName()
{
return 'user';
}
}
UserRelationType.php
class UserRelationType extends AbstractType
{
private $user;
public function __construct(SecurityContext $security){
$this->user = $security->getToken()->getUser();
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', 'hidden', array('data' => $this->user->getId()))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Website\CompanyBundle\Entity\User'
));
}
public function getName()
{
return 'user';
}
}
User.php entity
namespace Website\CompanyBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $name;
/**
* #ORM\OneToOne(targetEntity="Company", mappedBy="user")
*/
protected $company;
}
You map entity to form in UserRelationType. On save it try to set id on user entity. You must specify data transformer or use entity type if you need to select existed user.
If you want to set current user, better do it in event listener like pre_persist
your properties in your entities are protected but you have not created getters/setters for them. ( or you have not pasted your full code )
Therefore the form-builder is not able to access your user's properties.
At least the public function setId($id) in User.php is definitely missing!
That's why the exception is thrown saying:
Maybe you should create the method "setId()"?
Create getters and settes for every property using ...
app/console doctrine:generate:entities
or create them by hand...
User.php
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
// ...
I solved this problem without adding collections and just using Doctrine transitive persistence like described in this page: https://doctrine-orm.readthedocs.org/en/latest/reference/working-with-associations.html#transitive-persistence-cascade-operations
CompanyController.php
public function createIndex(Request $request){
$user = $this->getId();
$company = new Company();
if($user instanceof User){
$company->setUser($user);
}
$request = $this->getRequest();
$createForm = $this->createForm(new CompanyType(), $company);
if('POST' === $request->getMethod())
{
$createForm->bindRequest($request);
if($createForm->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($company);
$em->flush();
}
}
}
Thank you all for your help

Adding a file upload to a Symfony2 form throws an "Entity was not found" and a session is active error

I have a Symfony2 form that I want to add a file upload dialog to.
According to the Symfony docs (http://symfony.com/doc/2.0/cookbook/doctrine/file_uploads.html), I have created a Document class:
<?php
namespace Acme\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Document
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
public $path;
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
public function getWebPath()
{
return null === $this->path ? null : $this->getUploadDir().'/'.$this->path;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
return 'uploads/documents';
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
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) {
return;
}
$this->file->move($this->getUploadRootDir(), $this->path);
unset($this->file);
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath()) {
unlink($file);
}
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set path
*
* #param string $path
*/
public function setPath($path)
{
$this->path = $path;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
}
And a DocumentType form class:
<?php
namespace Acme\AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class DocumentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\AppBundle\Entity\Document',
));
}
public function getName()
{
return 'document_form';
}
}
However, when I add this to my existing entity and form class:
<?php
namespace Acme\AppBundle\Entity\Profile;
use Doctrine\ORM\Mapping as ORM;
use Acme\AppBundle\Entity\jDocument;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="UserProfile")
*/
class UserProfile extends GenericProfile
{
//... Other entity params
/**
* #ORM\OneToOne(cascade={"persist", "remove"}, targetEntity="Acme\AppBundle\Entity\Document")
* #ORM\JoinColumn(name="picture_id", referencedColumnName="id", onDelete="set null")
*/
protected $picture;
/**
* Set picture
*
* #param Acme\AppBundle\Entity\Document $picture
*/
//\Acme\AppBundle\Entity\Document
public function setPicture($picture)
{
$this->picture = $picture;
}
/**
* Get picture
*
* #return Acme\AppBundle\Entity\Document
*/
public function getPicture()
{
return $this->picture;
}
//... Other entity getters and setters
}
Whenever I submit the form, I get the following error:
ErrorException: Warning: ini_set(): A session is active. You cannot change the session module's ini settings at this time in /var/www/wolseley-integrated-services/builds/dev/app/cache/prod/classes.php line 421
But the page title is "Entity was not found. (500 Internal Server Error)".
Can anybody spot which entity it can't find? Or if that's even the issue?
I've done some googling and checked that session.auto_start is set to 0 in php.ini, I've cleared all my sessions and caches... I'm stumped!
It turns out I was getting this strange session error message because of an error page definition in my nginx config.
The entity not found issue was resolved by correcting some errors in my entities. The Symfony developer bar provided me with enough information to track the issue down.

Resources