JMSSerializer deserialize entity by id - symfony

i'm using JMSSerializer to deserialize a JSON request and i'm having troubles with ManyToOne relations. I would like to deserialize the relation entity from a id given. Example:
Class Game {
/**
* #var Team
*
* #ORM\ManyToOne(targetEntity="Team")
* #ORM\JoinColumn(name="home_team_id", referencedColumnName="id")
* #JMSSerializer\SerializedName("home")
*/
private $homeTeam;
/**
* #ORM\ManyToOne(targetEntity="Team")
* #ORM\JoinColumn(name="visitor_team_id", referencedColumnName="id")
* #JMSSerializer\SerializedName("visitor")
*/
private $visitorTeam;
}
So when i get this Json
{"home": "id1", "visitor": "id2"}
Get the related entities. Any clouds?? i can't figure it out
Thanks in advance

Custom serializer handler allows to do it.
At first, you need to create your own serialization handler. Something like this:
<?php
namespace AppBundle\Serializer\Handler;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
use JMS\Serializer\Context;
use JMS\Serializer\Exception\InvalidArgumentException;
use JMS\Serializer\GenericDeserializationVisitor;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\VisitorInterface;
use JMS\Serializer\GraphNavigator;
class EntityHandler implements SubscribingHandlerInterface
{
/**
* #var RegistryInterface
*/
protected $registry;
/**
* #return array
*/
public static function getSubscribingMethods()
{
$methods = [];
foreach (['json', 'xml', 'yml'] as $format) {
$methods[] = [
'type' => 'Entity',
'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
'format' => $format,
'method' => 'deserializeEntity',
];
$methods[] = [
'type' => 'Entity',
'format' => $format,
'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
'method' => 'serializeEntity',
];
}
return $methods;
}
/**
* EntityHandler constructor.
* #param RegistryInterface $registry
*/
public function __construct(RegistryInterface $registry)
{
$this->registry = $registry;
}
/**
* #param VisitorInterface $visitor
* #param $entity
* #param array $type
* #param Context $context
* #return mixed
*/
public function serializeEntity(VisitorInterface $visitor, $entity, array $type, Context $context)
{
$entityClass = $this->getEntityClassFromParameters($type['params']);
if (!$entity instanceof $entityClass) {
throw new InvalidArgumentException(
sprintf("Entity class '%s' was expected, but '%s' got", $entityClass, get_class($entity))
);
}
$entityManager = $this->getEntityManager($entityClass);
$primaryKeyValues = $entityManager->getClassMetadata($entityClass)->getIdentifierValues($entity);
if (count($primaryKeyValues) > 1) {
throw new InvalidArgumentException(
sprintf("Composite primary keys does'nt supported now (found in class '%s')", $entityClass)
);
}
if (!count($primaryKeyValues)) {
throw new InvalidArgumentException(
sprintf("No primary keys found for entity '%s')", $entityClass)
);
}
$id = array_shift($primaryKeyValues);
if (is_int($id) || is_string($id)) {
return $visitor->visitString($id, $type, $context);
} else {
throw new InvalidArgumentException(
sprintf(
"Invalid primary key type for entity '%s' (only integer or string are supported",
$entityClass
)
);
}
}
/**
* #param GenericDeserializationVisitor $visitor
* #param string $id
* #param array $type
*/
public function deserializeEntity(GenericDeserializationVisitor $visitor, $id, array $type)
{
if (null === $id) {
return null;
}
if (!(is_array($type) && isset($type['params']) && is_array($type['params']) && isset($type['params']['0']))) {
return null;
}
$entityClass = $type['params'][0]['name'];
$entityManager = $this->getEntityManager($entityClass);
return $entityManager->getRepository($entityClass)->find($id);
}
/**
* #param array $parameters
* #return string
*/
protected function getEntityClassFromParameters(array $parameters)
{
if (!(isset($parameters[0]) && is_array($parameters[0]) && isset($parameters[0]['name']))) {
throw new InvalidArgumentException('Entity class is not defined');
}
if (!class_exists($parameters[0]['name'])) {
throw new InvalidArgumentException(sprintf("Entity class '%s' is not found", $parameters[0]['name']));
}
return $parameters[0]['name'];
}
/**
* #param string $entityClass
* #return EntityManagerInterface
*/
protected function getEntityManager($entityClass)
{
$entityManager = $this->registry->getEntityManagerForClass($entityClass);
if (!$entityManager) {
throw new InvalidArgumentException(
sprintf("Entity class '%s' is not mannaged by Doctrine", $entityClass)
);
}
return $entityManager;
}
}
Then you should register it in your service configuration file. If you use yaml, it will be something like that:
custom_serializer_handle:
class: AppBundle\Serializer\Handler\EntityHandler
arguments: ['#doctrine']
tags:
- {name: 'jms_serializer.subscribing_handler'}
In your entity, define JMSSerializer Type annotation
/**
* #var Team
* * #ORM\ManyToOne(targetEntity="Team")
* #ORM\JoinColumn(name="home_team_id", referencedColumnName="id")
* #JMSSerializer\SerializedName("home")
* #JMSSerializer\Type("Entity<AppBundle\Entity\Team>")
* List item
*/
private $homeTeam;
Don't forget clear caches.
That's all.

Related

Method not found in entity Paginator from doctrine bundle

I'm trying to clean up some code done in a messy way and right now I'm having trouble with doctrine's paginator.
When I'm accessing a page that handle paginator in order to show all different articles of my blog I'm getting this error:
Neither the property "id" nor one of the methods "id()", "getid()"/"isid()"/"hasid()" or "__call()" exist and have public access in class "Doctrine\ORM\Tools\Pagination\Paginator".
In doctrine vendor bundle those methods are not set but my entity have them and I know that it is forbidden to edit a vendor file. I'm missing something because I don't know if I should extend my paginator entity and add those missing methods or is there a little bit more to do ?
I just started symfony and I know that my bases are not enough to understand it all by myself.
Thank you very much for you time and attention.
Here is my Article controller for route category:
/**
* #Route("/categorie/{id}", name="categorie")
*
* #param Request $request
* #param Helper $helper
* #param AuthorizationCheckerInterface $authChecker
* #param DocumentCategory $categorie
* #param TwitterService $twitterService
*
* #return RedirectResponse|Response
*/
public function categorie(
Request $request,
Helper $helper,
AuthorizationCheckerInterface $authChecker,
DocumentCategory $categorie,
TwitterService $twitterService
) {
if (!$authChecker->isGranted('IS_AUTHENTICATED_FULLY')
&& !$authChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
return $this->redirectToRoute('login');
}
$page = (int) ($request->get('page'));
if (0 === $page) {
$page = 1;
}
$userType = $this->getDoctrine()->getRepository('App:User')
->getManagerExpertCollabo($this->getUser());
$articleAlaUneListe = [];
$articleIdListe = $helper->getArticleIdAuth($authChecker);
$articleListe = $this->getDoctrine()
->getRepository('App:Document')
->getPage(
self::ITEM_PER_PAGE * ($page - 1),
self::ITEM_PER_PAGE,
'document.dateCreated',
'DESC',
'(documentCategory.id = \''.$categorie->getId().'\' and '.$helper->baseRequestArticle().')',
7,
[],
$articleIdListe
);
[$articlePopulaireListe, $categorieListe, $totalPage] = $this->getPopularArticleList(
$articleListe,
$helper,
$articleIdListe
);
$articlesList->getDocuments();
$feedData = $twitterService->getTwitterFeed();
return $this->render('article/list.html.twig', [
'pageClass' => 'backoffice withFooterLarge dashboard',
'totalPage' => $totalPage,
'page' => $page,
'feedData' => $feedData,
'categorieListe' => $categorieListe,
'categorie' => $categorie,
'articleAlaUneListe' => $articleAlaUneListe,
'articlePopulaireListe' => $articlePopulaireListe,
'articleListe' => $articleListe, ]);
}
Here is the document entity for categories field:
/**
* #ORM\JoinTable(name="ht_lk_document_category"),
* #ORM\ManyToMany(targetEntity="App\Entity\DocumentCategory", inversedBy="documents")
*/
private $categories;
/**
* #return Collection|array<DocumentCategory>
*/
public function getCategories(): Collection
{
return $this->categories;
}
public function setCategories($category): self
{
$this->categories = $category;
return $this;
}
Here is the DocumentCategory entity :
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Document", mappedBy="categories")
*/
private $documents;
/**
* #return Collection|Document[]
*/
public function getDocuments(): Collection
{
return $this->documents;
}
Here is the Document Repository :
public function getPage($first_result, $max_results, $orderby, $direction, $criteria, $documentType = null, $searchWordArray = [], $articleIdListe = '')
{
$qb = $this->createQueryBuilder('docArticle');
$qb->select('docArticle')
->addSelect('documentCategory', 'documentCategory')
->addSelect('user', 'user')
/*
if(sizeof($searchWordArray) > 0){
$fieldIndice = 1;
foreach($searchWordArray as $searchWord){
$qb->andWhere('(document.name_fr LIKE ?'.$fieldIndice.' or document.name_en LIKE ?'.$fieldIndice.' or document.content_fr LIKE ?'.$fieldIndice.' or document.content_en LIKE ?'.$fieldIndice.')');
$qb->setParameter($fieldIndice++, '%'.$searchWord.'%');
}
} */
->leftJoin('docArticle.categories', 'documentCategory')
->leftJoin('docArticle.author', 'user')
->setFirstResult($first_result)
->setMaxResults($max_results);
if (!empty($criteria)) {
$qb->where('('.$criteria.')');
}
if (!empty($orderby)) {
$qb->orderBy($orderby, $direction);
}
$pag = new Paginator($qb->getQuery());
$qb->setFirstResult(0);
$qb->setMaxResults(PHP_INT_MAX);
$sql = $qb->getQuery()->getSql();
if ('()' !== $articleIdListe) {
$qb->where('(docArticle.id IN '.$articleIdListe);
}
$compte = \count($qb->getQuery()->getScalarResult());
return ['page' => $pag, 'compte' => $compte];
}
And finally here is the Document Category Repository :
/**
* #param $first_result
* #param $max_results
* #param $orderby
* #param $direction
* #param $criteria
* #param int|null $documentType
* #param array $searchWordArray
* #param string $articleIdListe
*
* #return array
*/
public function getPage(
$first_result,
$max_results,
$orderby,
$direction,
$criteria,
$documentType = null,
$searchWordArray = [],
$articleIdListe = ''
) {
$qb = $this->createQueryBuilder('document');
$qb->select('document')
->addSelect('documentCategory', 'documentCategory')
->addSelect('user', 'user')
->addSelect('documentType', 'documentType');
if (\count($searchWordArray) > 0) {
$fieldIndice = 1;
foreach ($searchWordArray as $searchWord) {
$qb->andWhere(
'('
.'document.name_fr LIKE ?'.$fieldIndice
.' or document.name_en LIKE ?'.$fieldIndice
.' or document.content_fr LIKE ?'.$fieldIndice
.' or document.content_en LIKE ?'.$fieldIndice
.')'
);
$qb->setParameter($fieldIndice++, '%'.$searchWord.'%');
}
}
if ($documentType) {
if (\mb_strlen($articleIdListe) > 3) {
$qb->andWhere('(documentType.id = :documentType OR document.id IN '.$articleIdListe.')')
->setParameter('documentType', $documentType);
} else {
$qb->andWhere('(documentType.id = :documentType)')
->setParameter('documentType', $documentType);
}
}
$qb->leftJoin('document.categories', 'documentCategory')
->leftJoin('document.documentType', 'documentType')
->leftJoin('document.author', 'user')
->setFirstResult($first_result)
->setMaxResults($max_results)
->andWhere('document.documentType<>6');
if (!empty($criteria)) {
$qb->andWhere('('.$criteria.')');
}
if (!empty($orderby)) {
$qb->orderBy($orderby, $direction);
}
$sql = $qb->getQuery()->getSql();
$pag = new Paginator($qb->getQuery());
dump($pag);
$qb->setFirstResult(0);
$qb->setMaxResults(PHP_INT_MAX);
$sql = $qb->getQuery()->getSql();
$compte = \count($qb->getQuery()->getScalarResult());
return ['page' => $pag, 'compte' => $compte];
}
/**
* #param int|null $documentType
*
* #return array
*/
public function getArticleIdList($documentType = null)
{
$qb = $this->createQueryBuilder('document');
$qb->select('document.id');
if ($documentType) {
$qb->where('(document.documentType = :documentType)')
->setParameter('documentType', $documentType);
}
$compte = $qb->getQuery()->getScalarResult();
return $compte;
}
(I deleted all unnecessary method for this question)
Add the following code to the entity class from which you want to show data:
public function getId(): ?string
{
return $this->id;
}
Or
if you do not want the property "id" to be displayed in the view, comment out or delete the lines of code in your Twig template for the entity in question. For example, delete
<td>{{ Category.id }}</td>

JMSSerializerBundle deserialization skip groups exclusion on id property using DoctrineObjectConstructor

I'm using jms/serializer-bundle 2.4.3 on a symfony 4.2 and a I noticed an annoying problem in my application :
when I post an entity, the DoctrineObjectConstructor uses id in content to retrieve another entity and thus patch it while it is excluded by my security groups
see rather entity
class Entity
{
/**
* #var int
*
* #ORM\Column(name="id", type="int")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #Serializer\Groups({"GetEntity"})
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string")
* #Serializer\Groups({"GetEntity", "PostEntity"})
*/
private $name;
}
controller
/**
* #Route("/entity", name="post_entity", methods={"POST"})
*/
public function postEntity(Request $request, EntityManagerInterface $entityManager, SerializerInterface $serializer): JsonResponse
{
$deserializationContext = DeserializationContext::create();
$deserializationContext->setGroups(['PostEntity']);
$entity = $serializer->deserialize($request->getContent(), Entity::class, 'json', $deserializationContext);
$entityManager->persist($entity);
$entityManager->flush();
return $this->json($entity, Response::HTTP_OK, [], ['groups' => ['GetEntity']]);
}
I have some JMS configurations changes in services
jms_serializer.object_constructor:
alias: jms_serializer.doctrine_object_constructor
public: true
jms_serializer.unserialize_object_constructor:
class: App\Serializer\ObjectConstructor
If anyone can explain to me how to ignore the id in this case I'm open to any suggestions.
Regards and thanks for any help
To resolve, just add override in your services.yaml
jms_serializer.doctrine_object_constructor:
class: App\Serializer\DoctrineObjectConstructor
arguments:
- '#doctrine'
- '#jms_serializer.unserialize_object_constructor'
jms_serializer.object_constructor:
alias: jms_serializer.doctrine_object_constructor
and add a local DoctrineObjectConstructor updated to ignore entities without current deserialization group on id property
class DoctrineObjectConstructor implements ObjectConstructorInterface
{
const ON_MISSING_NULL = 'null';
const ON_MISSING_EXCEPTION = 'exception';
const ON_MISSING_FALLBACK = 'fallback';
private $fallbackStrategy;
private $managerRegistry;
private $fallbackConstructor;
/**
* Constructor.
*
* #param ManagerRegistry $managerRegistry Manager registry
* #param ObjectConstructorInterface $fallbackConstructor Fallback object constructor
* #param string $fallbackStrategy
*/
public function __construct(ManagerRegistry $managerRegistry, ObjectConstructorInterface $fallbackConstructor, $fallbackStrategy = self::ON_MISSING_NULL)
{
$this->managerRegistry = $managerRegistry;
$this->fallbackConstructor = $fallbackConstructor;
$this->fallbackStrategy = $fallbackStrategy;
}
/**
* {#inheritdoc}
*/
public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context)
{
// Locate possible ObjectManager
$objectManager = $this->managerRegistry->getManagerForClass($metadata->name);
if (!$objectManager) {
// No ObjectManager found, proceed with normal deserialization
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
}
// Locate possible ClassMetadata
$classMetadataFactory = $objectManager->getMetadataFactory();
if ($classMetadataFactory->isTransient($metadata->name)) {
// No ClassMetadata found, proceed with normal deserialization
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
}
// Managed entity, check for proxy load
if (!\is_array($data)) {
// Single identifier, load proxy
return $objectManager->getReference($metadata->name, $data);
}
// Fallback to default constructor if missing identifier(s)
$classMetadata = $objectManager->getClassMetadata($metadata->name);
$identifierList = [];
foreach ($classMetadata->getIdentifierFieldNames() as $name) {
$propertyGroups = [];
if ($visitor instanceof AbstractVisitor) {
/** #var PropertyNamingStrategyInterface $namingStrategy */
$namingStrategy = $visitor->getNamingStrategy();
$dataName = $namingStrategy->translateName($metadata->propertyMetadata[$name]);
$propertyGroups = $metadata->propertyMetadata[$name]->groups;
} else {
$dataName = $name;
}
if (!array_key_exists($dataName, $data) || true === empty(array_intersect($context->getAttribute('groups'), $propertyGroups))) {
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
}
$identifierList[$name] = $data[$dataName];
}
// Entity update, load it from database
$object = $objectManager->find($metadata->name, $identifierList);
if (null === $object) {
switch ($this->fallbackStrategy) {
case self::ON_MISSING_NULL:
return null;
case self::ON_MISSING_EXCEPTION:
throw new ObjectConstructionException(sprintf('Entity %s can not be found', $metadata->name));
case self::ON_MISSING_FALLBACK:
return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
default:
throw new InvalidArgumentException('The provided fallback strategy for the object constructor is not valid');
}
}
$objectManager->initializeObject($object);
return $object;
}
}

Symfony4 double entity on create

I am creating a simple mailer..
First you need to create a MailTemplate (Entity).
This exists of a subject, mailFrom and a Message.
Then you create the mail: This happens in two steps.
First you choose your account(s) to send to and your MailTemplate.
Then you redirect to another route where i set the subject, message and mailfrom, so i can adjust things.
When i send (Save the mail). it saved the mail but makes a copy of my MailTemplate and saved the Mailtemplate to.
So i got 1 mail and 2 Templates.
My Template entity
<?php
/**
* Created by PhpStorm.
* User: david
* Date: 26-6-2018
* Time: 20:13
*/
namespace App\Project\MailBundle\Entity;
use App\Project\BaseBundle\Entity\BaseEntity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Doctrine\ORM\Mapping as ORM;
/**
* Class MailTemplates
* #package App\Project\MailBundle\Entity
*/
/**
* #ORM\Entity(repositoryClass="App\Project\MailBundle\Repository\MailTemplatesRepository")
*/
class MailTemplate extends BaseEntity
{
public function __construct()
{
parent::__construct();
$this->active = true;
$this->bulkmails = new ArrayCollection();
$this->mails = new ArrayCollection();
}
/**
* #var string $template
*
* #ORM\Column(name="template", type="string", length=191, nullable=false)
*
*
*/
private $template;
/**
* #var string $mailFrom
*
* #ORM\Column(type="string", length=191, nullable=false, options={"default": "noreply#nachtpost.be"})
* #Assert\NotBlank()
*
*/
private $mailFrom;
/**
* #var string $mailSubject
*
* #ORM\Column(type="string", length=191, nullable=false)
* #Assert\NotBlank()
*
*/
private $mailSubject;
/**
* #var string $mailMessage
*
* #ORM\Column(type="text", nullable=false)
* #Assert\NotBlank()
*
*/
private $mailMessage;
/**
* #ORM\OneToMany(targetEntity="App\Project\MailBundle\Entity\Mail", mappedBy="mailTemplate", orphanRemoval=true)
*/
private $mails;
/**
* #ORM\OneToMany(targetEntity="App\Project\MailBundle\Entity\BulkMail", mappedBy="mailTemplate", orphanRemoval=true)
*/
private $bulkmails;
/**
* #return string
*/
public function getTemplate(): ? string
{
return $this->template;
}
/**
* #param string $template
*/
public function setTemplate(string $template): void
{
$this->template = $template;
}
/**
* #return mixed
*/
public function getMailFrom()
{
return $this->mailFrom;
}
/**
* #param mixed $mailFrom
*/
public function setMailFrom($mailFrom): void
{
$this->mailFrom = $mailFrom;
}
/**
* #return mixed
*/
public function getMailSubject()
{
return $this->mailSubject;
}
/**
* #param mixed $mailSubject
*/
public function setMailSubject($mailSubject): void
{
$this->mailSubject = $mailSubject;
}
/**
* #return mixed
*/
public function getMailMessage()
{
return $this->mailMessage;
}
/**
* #param mixed $mailMessage
*/
public function setMailMessage($mailMessage): void
{
$this->mailMessage = $mailMessage;
}
/**
* #return mixed
*/
public function getMails(): collection
{
return $this->mails;
}
/**
* #param mixed $mails
*/
public function setMails($mails)
{
$this->mails = $mails;
}
/**
* #return mixed
*/
public function getBulkmails(): collection
{
return $this->bulkmails;
}
/**
* #param mixed $bulkmails
*/
public function setBulkmails($bulkmails)
{
$this->bulkmails = $bulkmails;
}
public function __toString()
{
return $this->getTemplate();
}
}
My mailEntity
<?php
/**
* Created by PhpStorm.
* User: david
* Date: 26-6-2018
* Time: 20:13
*/
namespace App\Project\MailBundle\Entity;
use App\Project\BaseBundle\Entity\BaseEntity;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* Class BulkMail
* #package App\Project\MailBundle\Entity
*/
/**
* #ORM\Entity(repositoryClass="App\Project\MailBundle\Repository\BulkMailRepository")
*/
class BulkMail extends BaseEntity
{
public function __construct()
{
parent::__construct();
$this->mailTo = new ArrayCollection();
$this->active = true;
}
/**
* #ORM\ManyToOne(targetEntity="App\Project\MailBundle\Entity\MailTemplate", inversedBy="mails", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="bulkMail_id", referencedColumnName="id", nullable=false)
*/
private $mailTemplate;
/**
* #ORM\ManyToMany(targetEntity="App\Project\AccountBundle\Entity\Account")
* #ORM\JoinColumn(name="account_id", referencedColumnName="id", nullable=false)
* #Assert\NotBlank()
*/
private $mailTo;
/**
* #var string $mailFrom
*
* #ORM\Column(type="string", length=191, nullable=true)
*
*/
private $mailFrom;
/**
* #var string $mailSubject
*
* #ORM\Column(type="string", length=191, nullable=true)
*
*/
private $mailSubject;
/**
* #var string $mailMessage
*
* #ORM\Column(type="text", nullable=true)
*
*/
private $mailMessage;
/**
* #return mixed
*/
public function getMailTo()
{
return $this->mailTo;
}
/**
* #param mixed $mailTo
*/
public function setMailTo($mailTo): void
{
$this->mailTo = $mailTo;
}
/**
* #return mixed
*/
public function getMailFrom()
{
return $this->mailFrom;
}
/**
* #param mixed $mailFrom
*/
public function setMailFrom($mailFrom): void
{
$this->mailFrom = $mailFrom;
}
/**
* #return mixed
*/
public function getMailSubject()
{
return $this->mailSubject;
}
/**
* #param mixed $mailSubject
*/
public function setMailSubject($mailSubject): void
{
$this->mailSubject = $mailSubject;
}
/**
* #return mixed
*/
public function getMailMessage()
{
return $this->mailMessage;
}
/**
* #param mixed $mailMessage
*/
public function setMailMessage($mailMessage): void
{
$this->mailMessage = $mailMessage;
}
/**
* #param mixed $accountId
*/
public function setMail($account): void
{
$this->account = $account;
}
/**
* #return mixed
*/
public function getMailTemplate()
{
return $this->mailTemplate;
}
/**
* #param mixed $mailTemplate
*/
public function setMailTemplate($mailTemplate)
{
$this->mailTemplate = $mailTemplate;
}
public function __toString()
{
return $this->mailSubject;
}
}
My controller
<?php
/**
* Created by PhpStorm.
* User: david
* Date: 5-7-2018
* Time: 22:41
*/
namespace App\Project\MailBundle\Controller;
use App\Project\AccountBundle\Entity\Account;
use App\Project\MailBundle\Entity\BulkMail;
use App\Project\MailBundle\Entity\MailTemplate;
use App\Project\MailBundle\Forms\BulkAddMailAdminType;
use App\Project\MailBundle\Forms\bulkSelectmailAdminType;
use App\Project\BaseBundle\Controller\BaseController;
use Symfony\Component\HttpFoundation\Request;
class BulkMailController extends BaseController
{
public function BulkMailIndexAction()
{
$MailRepository = $this->getDoctrine()->getRepository(BulkMail::class);
$items = $MailRepository->findAll();
return $this->render('#ProjectMail/mails/bulk/index.html.twig', array(
'items' => $items
));
}
public function bulkSelectAction(Request $request) {
$mail = new BulkMail();
$form = $this->createForm(BulkSelectmailAdminType::class, $mail);
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$TemplateRepository = $this->getDoctrine()->getRepository(MailTemplate::class);
$tId = $mail->getMailTemplate()->getId();
$template = $TemplateRepository->findOneBy(array('id' => $tId));
$mail->setMailSubject($template->getMailSubject());
$mail->setMailFrom($template->getMailFrom());
$mail->setMailMessage($template->getMailMessage());
$mail->setMailTemplate($template);
$this->container->get('session')->set('Bmail', $mail);
$response = $this->redirectToRoute('mailBulkSend');
return $response;
}
}
$label = "Email opmaken";
$response = $this->render('#ProjectMail/mails/bulk/addBulk_select.html.twig', array(
'form' => $form->createView(),
'label' => $label
));
return $response;
}
/**
* #param Request $request
* #param \Swift_Mailer $mailer
* #return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function bulkMailAction(Request $request, \Swift_Mailer $mailer) {
$Bmail = $this->container->get('session')->get('Bmail');
if ($Bmail) {
$accounts = array();
$AccountRepository = $this->getDoctrine()->getRepository(Account::class);
$count = 0;
foreach ($Bmail->getMailTo() as $key => $account) {
$accounts[] = $AccountRepository->findOneById($account->getId());
$count ++;
}
$form = $this->createForm(BulkAddMailAdminType::class, $Bmail);
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$Bmail->setMailTo($accounts);
//dump($Bmail->getMailTemplate()); die;
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($Bmail);
$entityManager->flush();
// Get account emails in array
foreach($accounts as $key => $acc){
$persons[$acc->getEmail()] = $acc->getEmail();
}
$message = (new \Swift_Message($Bmail->getMailSubject()))
->setContentType("text/html")
->setFrom($Bmail->getMailFrom())
->setTo($persons)
->setBody(
$this->renderView(
'#ProjectTemplate/_templates/base_mail.html.twig', array(
'type' => 'emailDefault',
'template' => $Bmail
)
)
)
;
// $mailer->send($message);
$response = $this->redirectToRoute('mailsBulkList');
return $response;
}
}
$label = "Email verzenden";
$response = $this->render('#ProjectMail/mails/bulk/addMail.html.twig', array(
'form' => $form->createView(),
'mail' => $Bmail,
'accounts' => $accounts,
'label' => $label
));
} else {
$response = $this->redirectToRoute('mailBulkAdd');
}
return $response;
}
public function deleteAction(BulkMail $mail)
{
$em = $this->getDoctrine()->getManager();
$em->remove($mail);
$em->flush();
return $this->redirectToRoute('mailsBulkList');
}
}
I dont know why this happens and how to handle this right..
Suggestions? Ty in advance!!
Thanks for the help guys, really appreciated!
After some debugging i came out with this..
Not sure if its the way it should be but it works..
I save my mail in the first action and pass the id in my session, in the second action i pass the mail thru the form, (Can make changes if i need to) and send(Save it again).
Did some cleanup to :)
<?php
/**
* Created by PhpStorm.
* User: david
* Date: 5-7-2018
* Time: 22:41
*/
namespace App\Project\MailBundle\Controller;
use App\Project\AccountBundle\Entity\Account;
use App\Project\MailBundle\Entity\BulkMail;
use App\Project\MailBundle\Entity\MailTemplate;
use App\Project\MailBundle\Forms\BulkAddMailAdminType;
use App\Project\MailBundle\Forms\bulkSelectmailAdminType;
use App\Project\BaseBundle\Controller\BaseController;
use Symfony\Component\HttpFoundation\Request;
class BulkMailController extends BaseController
{
public function BulkMailIndexAction()
{
$MailRepository = $this->getDoctrine()->getRepository(BulkMail::class);
$items = $MailRepository->findAll();
return $this->render('#ProjectMail/mails/bulk/index.html.twig', array(
'items' => $items
));
}
public function bulkSelectAction(Request $request) {
$mail = new BulkMail();
$form = $this->createForm(BulkSelectmailAdminType::class, $mail);
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$template = $mail->getMailTemplate();
$mail->setMailSubject($template->getMailSubject());
$mail->setMailFrom($template->getMailFrom());
$mail->setMailMessage($template->getMailMessage());
$entityManager->persist($mail);
$entityManager->flush();
$this->container->get('session')->set('Bmail', $mail->getId());
$response = $this->redirectToRoute('mailBulkSend');
return $response;
}
}
$label = "Email opmaken";
$response = $this->render('#ProjectMail/mails/bulk/addBulk_select.html.twig', array(
'form' => $form->createView(),
'label' => $label
));
return $response;
}
/**
* #param Request $request
* #param \Swift_Mailer $mailer
* #return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function bulkMailAction(Request $request, \Swift_Mailer $mailer) {
$BmailId = $this->container->get('session')->get('Bmail');
if ($BmailId) {
$entityManager = $this->getDoctrine()->getManager();
$mailRepo = $this->getDoctrine()->getRepository(BulkMail::class);
$Bmail = $mailRepo->find($BmailId);
$accounts = array();
$AccountRepository = $this->getDoctrine()->getRepository(Account::class);
$count = 0;
foreach ($Bmail->getMailTo() as $key => $account) {
$accounts[] = $AccountRepository->findOneById($account->getId());
$count ++;
}
$form = $this->createForm(BulkAddMailAdminType::class, $Bmail);
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$Bmail->setMailTo($accounts);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($Bmail);
$entityManager->flush();
// Get account emails in array
foreach($accounts as $key => $acc){
$persons[$acc->getEmail()] = $acc->getEmail();
}
$message = (new \Swift_Message($Bmail->getMailSubject()))
->setContentType("text/html")
->setFrom($Bmail->getMailFrom())
->setTo($persons)
->setBody(
$this->renderView(
'#ProjectTemplate/_templates/base_mail.html.twig', array(
'type' => 'emailDefault',
'template' => $Bmail
)
)
)
;
// $mailer->send($message);
$response = $this->redirectToRoute('mailsBulkList');
return $response;
}
}
$label = "Email verzenden";
$response = $this->render('#ProjectMail/mails/bulk/addMail.html.twig', array(
'form' => $form->createView(),
'mail' => $Bmail,
'accounts' => $accounts,
'label' => $label
));
} else {
$response = $this->redirectToRoute('mailBulkAdd');
}
return $response;
}
public function deleteAction(BulkMail $mail)
{
$em = $this->getDoctrine()->getManager();
$em->remove($mail);
$em->flush();
return $this->redirectToRoute('mailsBulkList');
}
}

Could not determine access type for property "file"

I have a little problem with my image upload, can u help me please:
Could not determine access type for property "file".
Controller
/**
* Creates a new Produits entity.
*
*/
public function createAction(Request $request)
{
$entity = new Produits();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('adminProduits_show', array('id' => $entity->getId())));
}
return $this->render('EcommerceBundle:Administration:Produits/layout/new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Creates a form to create a Produits entity.
*
* #param Produits $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(Produits $entity)
{
$form = $this->createForm(ProduitsType::class, $entity);
$form->add('submit', SubmitType::class, array('label' => 'Ajouter'));
return $form;
}
/**
* Displays a form to create a new Produits entity.
*
*/
public function newAction()
{
$entity = new Produits();
$form = $this->createCreateForm($entity);
return $this->render('EcommerceBundle:Administration:Produits/layout/new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Form
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class, array('data_class' => null))
->add('name', TextType::class)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ecommerce\EcommerceBundle\Entity\Media'
));
}
/**
* #return string
*/
public function getName()
{
return 'ecommerce_ecommercebundle_media';
}
Entity
/**
* #ORM\Column(name="name",type="string",length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\Column(type="string",length=255, nullable=true)
*/
private $path;
/**
* #Assert\File(
* maxSize = "1024k",
* mimeTypes = {"image/png", "image/jpg", "image/bmp"},
* mimeTypesMessage = "Please upload a valid PDF"
* )
*/
public $file;
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()
{
return $this->name;
}
public function getFile()
{
return $this->file;
}
/**
* Set path
*
* #param string $path
* #return String
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Set alt
*
* #param string $alt
* #return String
*/
public function setAlt($alt)
{
$this->alt = $alt;
return $this;
}
/**
* Set name
*
* #param string $name
* #return String
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Set updateAt
*
* #param \DateTime $updateAt
*
* #return Media
*/
public function setUpdateAt($updateAt)
{
$this->updateAt = $updateAt;
return $this;
}
/**
* Get updateAt
*
* #return \DateTime
*/
public function getUpdateAt()
{
return $this->updateAt;
}
Thanks for your help guys :)
Please add "'mapped' => false," to form builder.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class,
array(
'data_class' => null,
'mapped' => false,
))
->add('name', TextType::class)
;
}
Those who say that it is wrong to dissolve, see the test there. I'm not the one who made it wrong.
Test code:
https://github.com/symfony/property-access/blob/master/Tests/PropertyAccessorCollectionTest.php#L151
A second solution is to add function setXxx to the property that gives an error in class Entity.
public $xxx;
public function setXxx(Array $xxx)
{
$this->xxx = $xxx;
}
Or
public function __construct()
{
$this->xxx = new ArrayCollection();
}
Video Link: https://knpuniversity.com/screencast/doctrine-relations/create-genus-note
My English is bad, I can tell you.
Setting mapped => false is not the real solution, because the field IS mapped to the entity.
In my case, I have a OneToMany relation, so I need the field mapped in order to use the 'cascase' => {"all"} option.
If the field is not mapped, then you must to persist the related entity manually. That's no good.
Stumbled on this while searching solution for my problem, that was shooting the same error and I was using also ArrayCollection class, so this may be helpful to someone:
My field in ArrayCollection is called $files, so I had to add constructor like this:
use Doctrine\Common\Collections\ArrayCollection;
public function __construct()
{
$this->files = new ArrayCollection();
}
Then I added add and remove methods, like this:
public function addFile(MyFile $file) : self
{
$file->setParentMyItem($this); // Setting parent item
$this->files->add($file);
return $this;
}
public function removeFile(MyFile $file) : self
{
$this->files->removeElement($file);
return $this;
}
But catch is that even my field name was $files I had to name add and remove methods addFile() and removeFile(), without 's' at end, which is totally not logical to me, but that solved the problem.
Same error for me but on a OneToMany relation. Although setting "mapped => false" also solved the error there is another option. I only had a getter, adder and remover method on the entity. I needed to add a "setter" too
In my case it was:
/**
* #param ArrayCollection|SubStatusOptions[]
*/
public function setSubStatusOptions(ArrayCollection $subStatusOptions)
{
$this->subStatusOptions = $subStatusOptions;
}
This happened because I had an entity property but the getter/setters were missing. I used:
php bin/console make:entity --regenerate MyBundle\\Entity\\MyEntity
to regenerate them

Symfony2: do not update a form field if not provided

I have a form for my "Team" entity. This entity has an "image" field. This field is required on creation process, but not required on edit process. But right now, during edit process, if I don't provide any image in the file input, the empty input is still persisted, and so my database field is emptied during the process.
How can I do to avoid persistence of this field, if nothing is provided in the form file input? So the entity keeps its old value for this field. Of course, if a file is provided, I want him to erase the old one.
My controller looks like this:
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$em->persist($team);
$em->flush();
...
}
}
and part of my entity, dealing with image (I'm pretty sure I have to do something in here, but don't know what exactly):
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function uploadImage() {
// the file property can be empty if the field is not required
if (null === $this->image) {
return;
}
if(!$this->id){
$this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
}else{
$this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
}
$this->setImage($this->image->getClientOriginalName());
}
EDIT
Ok, I did some changes to this answer's code code, because apparently the event listener asks for a FormEvent instance in his callback, not a FormInterface instance.
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
// Retrieve submitted data
$form = $event->getForm();
$item = $event->getData();
// Test if upload image is null (maybe adapt it to work with your code)
if (null !== $form->get('image')->getData()) {
var_dump($form->get('image')->getData());
die('image provided');
$item->setImage($form->get('image')->getData());
}
});
When I provide an image, the script goes into the test, and die(), as expected. When I do not provide any file, the script doesn't go into the test if(), but my field in the database is still erased with an empty value. Any idea?
And as asked below, here is the Form
// src/Van/TeamsBundle/Form/TeamEditType.php
namespace Van\TeamsBundle\Form;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class TeamEditType extends TeamType // Ici, on hérite de ArticleType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// On fait appel à la méthode buildForm du parent, qui va ajouter tous les champs à $builder
parent::buildForm($builder, $options);
// On supprime celui qu'on ne veut pas dans le formulaire de modification
$builder->remove('image')
->add('image', 'file', array(
'data_class' => null,
'required' => false
))
;
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
// Retrieve submitted data
$form = $event->getForm();
$item = $event->getData();
// Test if upload image is null (maybe adapt it to work with your code)
if (null !== $form->get('image')->getData()) {
var_dump($form->get('image')->getData());
die('image provided');
$item->setImage($form->get('image')->getData());
}
});
}
// On modifie cette méthode car les deux formulaires doivent avoir un nom différent
public function getName()
{
return 'van_teamsbundle_teamedittype';
}
}
and the whole Team entity:
<?php
namespace Van\TeamsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Team
*
* #ORM\Table()
* #ORM\HasLifecycleCallbacks
* #ORM\Entity
* #ORM\Entity(repositoryClass="Van\TeamsBundle\Entity\TeamRepository") #ORM\Table(name="van_teams")
*/
class Team
{
/**
* #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=100)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="countryCode", type="string", length=2)
*/
private $countryCode;
/**
* #ORM\ManyToOne(targetEntity="Van\TeamsBundle\Entity\Game")
* #ORM\JoinColumn(nullable=false)
*/
private $game;
/**
* #ORM\ManyToOne(targetEntity="Van\TeamsBundle\Entity\Statut")
* #ORM\JoinColumn(nullable=false)
*/
private $statut;
/**
* #var string $image
* #Assert\File( maxSize = "1024k", mimeTypesMessage = "Please upload a valid Image")
* #ORM\Column(name="image", type="string", length=255)
*/
private $image;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Team
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set countryCode
*
* #param string $countryCode
* #return Team
*/
public function setCountryCode($countryCode)
{
$this->countryCode = $countryCode;
return $this;
}
/**
* Get countryCode
*
* #return string
*/
public function getCountryCode()
{
return $this->countryCode;
}
/**
* Set image
*
* #param string $image
* #return Team
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set game
*
* #param \Van\TeamsBundle\Entity\Game $game
* #return Team
*/
public function setGame(\Van\TeamsBundle\Entity\Game $game)
{
$this->game = $game;
return $this;
}
/**
* Get game
*
* #return \Van\TeamsBundle\Entity\Game
*/
public function getGame()
{
return $this->game;
}
/**
* Set statut
*
* #param \Van\TeamsBundle\Entity\Statut $statut
* #return Team
*/
public function setStatut(\Van\TeamsBundle\Entity\Statut $statut)
{
$this->statut = $statut;
return $this;
}
/**
* Get statut
*
* #return \Van\TeamsBundle\Entity\Statut
*/
public function getStatut()
{
return $this->statut;
}
public function getFullImagePath() {
return null === $this->image ? null : $this->getUploadRootDir(). $this->image;
}
protected function getUploadRootDir() {
// the absolute directory path where uploaded documents should be saved
// return $this->getTmpUploadRootDir();
return __DIR__ . '/../../../../web/uploads/';
}
protected function getTmpUploadRootDir() {
// the absolute directory path where uploaded documents should be saved
return __DIR__ . '/../../../../web/uploads_tmp/';
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function uploadImage() {
// the file property can be empty if the field is not required
if (null === $this->image) {
return;
}
if(!$this->id){
$this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
}else{
$this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
}
$this->setImage($this->image->getClientOriginalName());
}
/**
* #ORM\PostPersist()
*/
public function moveImage()
{
if (null === $this->image) {
return;
}
if(!is_dir($this->getUploadRootDir())){
mkdir($this->getUploadRootDir());
}
copy($this->getTmpUploadRootDir().$this->image, $this->getFullImagePath());
unlink($this->getTmpUploadRootDir().$this->image);
}
/**
* #ORM\PreRemove()
*/
public function removeImage()
{
unlink($this->getFullImagePath());
rmdir($this->getUploadRootDir());
}
}
EDIT 2
I did that. When I provide an image, it is saved in the image field in the database and redirection to my index page occurs. When I don't provide any image, redirection doesn't occur, and the following message appears above my file input in my form: "The file could not be found." In my TeamEditType class, I did the following, so the image should not be required.
$builder->remove('image')
->add('image', 'file', array(
'data_class' => null,
'required' => false
))
;
As of Symfony 2.3, you can simply use the PATCH http method, as documented here.
$form = $this->createForm(FooType::class, $foo, array(
'action' => $this->generateUrl('foo_update', array('id' => $foo->getId())),
'method' => 'PATCH',
));
It's an easy way to make a partial update of an entity using your main form, without rendering all the fields.
One approach in Symfony 2.4 (further information in Symfony2 coockbook) :
public function buildForm(FormBuilderInterface $builder, array $options)
{
// $builder->add() ...
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormInterface $form) {
// Retrieve submitted data
$form = $event->getForm();
$image = $form->getData();
// Test if upload image is null (maybe adapt it to work with your code)
if (null !== $form->get('uploadImage')->getData()) {
$image->setUploadImage($form->get('uploadImage')->getData());
}
});
}
Edit
It seems that you already test your prepersit data. Try the following :
public function setImage($image)
{
if($image !== null) {
$this->image = $image;
return $this;
}
}
I'm using symfony 3.3 and faced the same issue, below is the solution how I overcome it.
You need to create a extra property for image uploading in your entity other than image property, something like $file;
Product.php
/**
* #var string
*
* #ORM\Column(name="image", type="string")
*
*/
private $image;
/**
*
* #Assert\File(mimeTypes={ "image/jpeg", "image/jpg", "image/png" })
*/
private $file;
public function setFile($file)
{
$this->file = $file;
return $this;
}
public function getFile()
{
return $this->file;
}
// other code i.e image setter getter
...
ProductType.php
$builder->add('file', FileType::class, array(
'data_class' => null,
'required'=>false,
'label' => 'Upload Image (jpg, jpeg, png file)')
);
Form.html.twig
<div class="form-group">
{{ form_label(form.file) }}
{{ form_widget(form.file, {'attr': {'class': 'form-control'}}) }}
</div>
Finally ProductController.php
...
if ($form->isSubmitted() && $form->isValid()) {
$file = $item->getFile();
if($file instanceof UploadedFile) {
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move(
$this->getParameter('upload_directory'),
$fileName
);
$item->setImage($fileName);
}
...
}
...
Little more about upload_directory
app/config/config.html
parameters:
upload_directory: '%kernel.project_dir%/web/uploads'
Create a uploads directory in web dir.
I resolved this issue by using PHP Reflection API. My approach was to browse the attributes of the entity class and replace the values not provided in the query with those already saved.
/**
* #param \ReflectionClass $reflectionClass
* #param $entity
* #param Request $request
* #return Request
*/
public function formatRequest($class, $entity, Request $request){
$reflectionClass = new \ReflectionClass('AppBundle\Entity\\'.$class);
foreach ($reflectionClass->getProperties() as $attribut)
{
$attribut->setAccessible(true); // to avoid fatal error when trying to access a non-public attribute
if($request->request->get($attribut->getName()) == null) { // if the attribute value is not provided in the request
$request->request->set($attribut->getName(), $attribut->getValue($entity));
}
}
return $request;
}
And then I use it like this :
$request = $this->formatRequest("EntityName", $entity, $request);
This is really generic.

Resources