I have some logic to apply after getting entities from database( by findAll() ) and before serializing the result to json.
I want to add translation on some fields. I know that I can do it manually by iterating on each entity and apply my logic in controller. But I need a better way to do it.
Is there a suggestions to make this automatic ?
I've got similar problem and tried to resolve this using custom handler but with no success, so i created compiler pass and override JsonSerializationVisitor where string values are serialized with TranslatableJsonSerializationVisitor class:
namespace tkuska\DemoBundle\Serialization;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\Context;
class TranslatableJsonSerializationVisitor extends JsonSerializationVisitor
{
/**
* #var \Symfony\Component\Translation\Translator;
*/
private $translator;
public function visitString($data, array $type, Context $context)
{
if (in_array('translatable', $type['params'])) {
return (string) $this->translator->trans($data);
}
return (string) $data;
}
public function setTranslator(\Symfony\Component\Translation\Translator $translator)
{
$this->translator = $translator;
}
}
and compiler:
namespace tkuska\DemoBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class TranslatableJsonSerializationCompiler implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$jmsJsonSerializationVisitor = $container->getDefinition('jms_serializer.json_serialization_visitor');
$jmsJsonSerializationVisitor->setClass('tkuska\DemoBundle\Serialization\TranslatableJsonSerializationVisitor');
$jmsJsonSerializationVisitor->addMethodCall('setTranslator', array(new Reference('translator')));
}
}
in entity i set annotation for type 'string' with param 'translatable'
/**
* #VirtualProperty
* #Groups({"details"})
* #Type("string<'translatable'>")
* #return order status
*/
public function getOrderStatus(){
return 'status.ordered';
}
Of course 'status.ordered' is my translation key.
Thank you #zizoujab. Very useful post. I made a small improvement to it to call parent method and to unset my parameters, so that i can use this way of altering data on more complex types like array, that already have parameters and used
/**
* #VirtualProperty
* #Groups({"details"})
* #Type("string<translatable>")
* #return order status
*/
instead of
/**
* #VirtualProperty
* #Groups({"details"})
* #Type("string<'translatable'>")
* #return order status
*/
to convert the string 'translatable' into a parameter name instead of parameter value thus you can pass more complex parameters like this, and allow me to unset this parameters before calling the parent method.
/**
* #VirtualProperty
* #Groups({"details"})
* #Type("string<translatable<set of parameters>>")
* #return order status
*/
Code:
<?php
namespace Mktp\DefaultBundle\Service\JmsSerializer;
use JMS\Serializer\Context;
use JMS\Serializer\JsonSerializationVisitor;
use Symfony\Component\Translation\Translator;
class TranslatableJsonSerializationVisitor extends JsonSerializationVisitor
{
/**
* #var Translator;
*/
private $translator;
/**
* #param string $data
* #param array $type
* #param Context $context
* #return string
*/
public function visitString($data, array $type, Context $context)
{
$translatable = $this->getParameters('translatable', $type['params']);
if (count($translatable)) {
$data = (string)$this->translator->trans($data);
}
return parent::visitString($data, $type, $context);
}
/**
* #param array $data
* #param array $type
* #param Context $context
* #return array|\ArrayObject|mixed
*/
public function visitArray($data, array $type, Context $context)
{
$translatable = $this->getParameters('translatable', $type['params']);
if (count($translatable)) {
foreach ($data as $key => $value) {
if (is_string($value)) {
$data[$key] = (string)$this->translator->trans($value);
}
}
}
return parent::visitArray($data, $type, $context);
}
/**
* #param Translator $translator
*/
public function setTranslator(Translator $translator)
{
$this->translator = $translator;
}
/**
* #param string $type
* #param array $parameters
* #param bool $unsetParameters
* #return array
*/
protected function getParameters($type, &$parameters, $unsetParameters = true)
{
$result = array();
foreach ($parameters as $key => $parameter) {
if ($parameter['name'] == $type) {
$result[] = $parameter;
if ($unsetParameters) {
unset($parameters[$key]);
}
}
}
$parameters = array_values($parameters);
return $result;
}
}
Related
I'm trying to normalize the responses of my controllers and most of them work except for this one because it has a DateTime object. This is the class Touch:
<?php
namespace App\Entity;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\TouchRepository")
*/
class Touch implements \JsonSerializable
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="datetime")
*/
private $time;
/**
* #ORM\Column(type="string", length=255)
*/
private $toucher;
private $accountId;
/**
* #ORM\ManyToOne(targetEntity="Cabinet")
*/
private $cabinet;
/**
* Touch constructor.
* #param DateTime $time
* #param string $toucher
* #param Cabinet $cabinet
* #param int $id
*/
public function __construct(DateTime $time, string $toucher, Cabinet $cabinet = null, int $id = null)
{
$this->time = $time;
$this->toucher = $toucher;
$this->cabinet = $cabinet;
$this->id = $id;
}
public function getId(): int
{
return $this->id;
}
public function setId(int $id): self
{
$this->id = $id;
return $this;
}
public function getTime(): DateTime
{
return $this->time;
}
public function getToucher(): string
{
return $this->toucher;
}
public function getCabinet(): Cabinet
{
return $this->cabinet;
}
public function setCabinet(Cabinet $cabinet): self
{
$this->cabinet = $cabinet;
$this->accountId = $cabinet->getId();
return $this;
}
public function getAccountId(): int
{
return $this->accountId;
}
public function setAccountId(int $accountId): self
{
$this->accountId = $accountId;
return $this;
}
public function jsonSerialize()
{
return get_object_vars($this);
}
}
And I made a normalizer, TouchNormalizer.php, for it as follows:
<?php
namespace App\Normalizer;
use App\Entity\Touch;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
class TouchNormalizer implements NormalizerInterface
{
/**
* Normalizes an object into a set of arrays/scalars.
*
* #param Touch $object Object to normalize
* #param string $format Format the normalization result will be encoded as
* #param array $context Context options for the normalizer
*
* #return array|string|int|float|bool
*/
public function normalize($object, $format = null, array $context = []): array
{
return [
'id' => $object->getId(),
'time' => $object->getTime(),
'toucher' => $object->getToucher(),
'cabinet' => $object->getCabinet(),
'accountId' => $object->getAccountId()
];
}
/**
* Checks whether the given class is supported for normalization by this normalizer.
*
* #param mixed $data Data to normalize
* #param string $format The format being (de-)serialized from or into
*
* #return bool
*/
public function supportsNormalization($data, $format = null)
{
return $data instanceof Touch && $format === 'json';
}
}
Then in the controller, TouchController, I have this:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\TouchRepository;
use App\Entity\Bedroom;
use Symfony\Component\Serializer\Serializer;
use App\Http\SerializedJsonResponse;
use App\Normalizer\TouchNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
class TouchController extends AbstractController
{
/**
* #Route("/Bedroom/{bedroomId}/touchs", methods={"GET"})
* #param \App\Controller\AuthenticationController $apiKeyAuthenticator
* #param Request $request
* #param Bedroom $bedroomId
* #param TouchRepository $touchRepository
* #return SerializedJsonResponse
*/
public function AuthenticateAPI(AuthenticationController $apiKeyAuthenticator, Request $request, Bedroom $bedroomId, TouchRepository $touchRepository)
{
$AuthenticatorObject = $apiKeyAuthenticator->checkAuthentication($request);
if (isset($AuthenticatorObject['success'])) {
return new SerializedJsonResponse(new Serializer([new TouchNormalizer()], [new JsonEncoder()]), $touchRepository->findTouchsByAccount($bedroomId));
}
return new SerializedJsonResponse(new Serializer([new TouchNormalizer()], [new JsonEncoder()]), $AuthenticatorObject, 401);
}
}
This returns the aforementioned error:
Could not normalize object of type DateTime, no supporting normalizer found.
I tried using the DateTimeNormalizer by replacing TouchNormalizer by DateTimeNormalizer but this returns the time in an odd form, namely:
"time": "2019-06-12T09:51:22+00:00,"
instead of the original
"time": {
"date": "2019-06-12 09:51:22.000000",
"timezone_type": 3,
"timezone": "UTC"
},
How could I keep my original DateTime format and normalize the DateTime object properly?
I am attempting to create a module which defines it's own custom Type and associated Field plugins.
When installed, GraphQLi reports the following error in the console:
Uncaught Error: CustomTypeInterface fields must be an object with field names as keys or a function which returns such an object.
Drupal 8.61. I have tried on both GraphQL 3.0-RC2 and 3.x-Dev. Any help would be much appreciated. Thanks.
My code is as follows:
/graphql_custom.info.yml
name: GraphQL Custom Type Example
type: module
description: ''
package: GraphQL
core: 8.x
dependencies:
- graphql_core
/src/CustomObject.php
namespace Drupal\graphql_custom;
class CustomObject {
protected $data;
function __construct(String $data) {
$this->data = $data;
}
function getData() {
return $this->data;
}
}
/src/Plugin/GraphQL/Fields/CustomField.php
<?php
namespace Drupal\graphql_custom\Plugin\GraphQL\Fields;
use Drupal\graphql_custom\CustomObject;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
use GraphQL\Type\Definition\ResolveInfo;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Created Custom Object with argument as data.
*
* #GraphQLField(
* id = "custom_field",
* secure = true,
* name = "customfield",
* type = "CustomType",
* nullable = true,
* arguments = {
* "argument" = "String!"
* }
* )
*/
class CustomField extends FieldPluginBase implements ContainerFactoryPluginInterface {
/**
* {#inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition
);
}
/**
* {#inheritdoc}
*/
protected function isLanguageAwareField() {
return FALSE;
}
/**
* {#inheritdoc}
*/
public function resolve($value, array $args, ResolveContext $context, ResolveInfo $info) {
return parent::resolve($value, $args, $context, $info);
}
/**
* {#inheritdoc}
*/
public function resolveValues($value, array $args, ResolveContext $context, ResolveInfo $info) {
$arg = $args['argument'];
$object = new CustomObject($arg);
yield $object;
}
}
/src/Plugin/GraphQL/Fields/CustomFieldData.php
<?php
namespace Drupal\graphql_custom\Plugin\GraphQL\Fields;
use Drupal\graphql_custom\CustomObject;
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use GraphQL\Type\Definition\ResolveInfo;
/**
* Custom Type Data Field
*
* #GraphQLField(
* id = "custom_field_data",
* secure = true,
* name = "data",
* type = "String",
* parents = {"CustomType"}
* )
*/
class CustomFieldData extends FieldPluginBase {
/**
* {#inheritdoc}
*/
protected function resolveValues($value, array $args, $context, $info) {
if ($value instanceOf CustomObject) {
yield (string) $value->getData();
} else {
yield (string) "Empty";
}
}
}
/src/Plugin/GraphQL/Interfaces/CustomTypeInterface.php
<?php
namespace Drupal\graphql_custom\Plugin\GraphQL\Interfaces;
use Drupal\graphql_custom\CustomObject;
use Drupal\graphql\Annotation\GraphQLInterface;
use Drupal\graphql\Plugin\GraphQL\Interfaces\InterfacePluginBase;
/**
* Interface for Custom Type.
*
* For simplicity reasons, this example does not utilize dependency injection.
*
* #GraphQLInterface(
* id = "custom_type_interface",
* name = "CustomTypeInterface"
* )
*/
class CustomTypeInterface extends InterfacePluginBase {
/**
* {#inheritdoc}
*/
public function resolveType($object) {
if ($object instanceof CustomObject) {
$schemaManager = \Drupal::service('graphql_core.schema_manager');
return $schemaManager->findByName('CustomType', [
GRAPHQL_CORE_TYPE_PLUGIN,
]);
}
}
}
/src/Plugin/GraphQL/Types/CustomType.php
<?php
namespace Drupal\graphql_custom\Plugin\GraphQL\Types;
use Drupal\graphql_custom\CustomObject;
use Drupal\graphql\Plugin\GraphQL\Types\TypePluginBase;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use GraphQL\Type\Definition\ResolveInfo;
/**
* GraphQL Custom Type.
*
* #GraphQLType(
* id = "custom_type",
* name = "CustomType",
* interfaces = {"CustomTypeInterface"}
* )
*/
class CustomType extends TypePluginBase {
/**
* {#inheritdoc}
*/
public function applies($object, ResolveContext $context, ResolveInfo $info) {
return $object instanceof CustomObject;
}
}
With your fields you should use interface reference in parents instead of type:
parents = {"CustomTypeInterface"}
Another way is to remove interface and use direct type reference as mentioned in your example.
Sorry if this question is too messy, but is there a common way to handle form request in Symfony ? (I use SF 4).
For now, I have the logic in my controller :
$formBooking = $this->createForm(BookingType::class);
$formBooking->handleRequest($request);
if ($formBooking->isSubmitted() && $formBooking->isValid()) {
// perform actions ...
I have several forms on the same page, so my controller gets bigger and bigger.
I wanted to creation a new folder like Action and put the logical here.
And do in my controller :
$formBooking = $this->createForm(BookingType::class);
// $bookingAction = new App\Action\BookingAction
$bookingAction->handleRequest($formBooking, $request);
I just want to know if there any "official" way for this ?
You can create an abstract "BaseController", and inherit your other controller. I don't think this is an "official" way, but it seems proper.
In my example, I have set my entity manager, my form factory and my routing as services. Also, it's called BaseManager, as all my logic are in manager file, while my controller doesn't have code (kind of just calling my manager, except for specific cases)
To do so, simply reference your service, and let your controller be the master, but a clean and simple one.
## Controller##
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Category;
use AppBundle\Form\CategoryType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
/**
* #Route("/admin/category")
*/
class CategoryController extends Controller
{
/**
* #Template()
* #Route("/", name="category_index")
* #return array
*/
public function indexAction()
{
$categories = $this->get('app.category.manager')->find();
return array('categories' => $categories);
}
/**
* #Template()
* #Route("/", name="category_menu")
* #return array
*/
public function menuAction()
{
$categories = $this->getDoctrine()->getRepository('AppBundle:Category')->findAllOrdered("ASC");
return array('categories' => $categories);
}
/**
* #Template()
* #Route("/icones/glyficones", name="category_icones_glyficones")
* #return array
*/
public function fontawesomeAction()
{
return array();
}
/**
* #Template()
* #Route("/icones/fontawesome", name="category_icones_fontawesome")
* #return array
*/
public function glyficoneAction()
{
return array();
}
/**
* #Template()
* #Route("/new", name="category_create")
* #param Request $request
* #return array
*/
public function newAction(Request $request)
{
return $this->get('app.category.manager')->save($request);
}
/**
* #Template()
* #Route("/{id}/edit", name="category_edit")
* #param Request $request
* #param $id
* #return array
*/
public function editAction(Request $request, $id)
{
return $this->get('app.category.manager')->edit($request, $id);
}
/**
* #Template()
* #Route("/{id}/delete", name="category_delete")
* #param Request $request
* #param $id
* #return array
*/
public function deleteAction(Request $request, $id)
{
return $this->get('app.category.manager')->delete($request, $id);
}
}
Base Manager
<?php
namespace AppBundle\Manager;
use AppBundle\Form\CategoryType;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
abstract class BaseManager
{
protected $em;
protected $formFactory;
protected $router;
/**
* CategoryManager constructor.
* #param $em
* #param $formFactory
* #param Router $router
*/
public function __construct($em, $formFactory, Router $router)
{
$this->em = $em;
$this->formFactory = $formFactory;
$this->router = $router;
}
/**
* #param $entity
*/
protected function persistAndFlush($entity)
{
$this->em->persist($entity);
$this->em->flush();
}
/**
* #param $entity
*/
protected function removeAndFlush($entity)
{
$this->em->remove($entity);
$this->em->flush();
}
/**
* #param Request $request
* #param Form $form
* #param $entity
* #param $path
* #return array|RedirectResponse
*/
protected function handleBaseForm(Request $request, Form $form, $entity, $path)
{
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->persistAndFlush($entity);
return new RedirectResponse($this->router->generate($path));
}
return array('form' => $form->createView());
}
/**
* #param $route
* #return RedirectResponse
*/
protected function redirect($route)
{
return new RedirectResponse($this->router->generate($route));
}
}
And here an example, a simple crud froma "Category" entity.
Specific Entity Manager
<?php
namespace AppBundle\Manager;
use AppBundle\Entity\Category;
use AppBundle\Form\CategoryType;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Class CategoryManager
* #package AppBundle\Manager
*/
class CategoryManager extends BaseManager
{
/**
* CategoryManager constructor.
* #param $em
* #param $formFactory
* #param Router $router
*/
public function __construct($em, $formFactory, Router $router)
{
parent::__construct($em, $formFactory, $router);
}
/**
* #return mixed
*/
public function find()
{
return $this->em->getRepository('AppBundle:Category')->findAll();
}
/**
* #param Request $request
* #return array
*/
public function save(Request $request)
{
$category = new Category();
return $this->handleForm($request, $category);
}
/**
* #param Request $request
* #param $id
* #return array|RedirectResponse
*/
public function edit(Request $request, $id)
{
$category = $this->em->getRepository('AppBundle:Category')->find($id);
return $this->handleForm($request, $category);
}
/**
* #param Request $request
* #param $id
* #return RedirectResponse
*/
public function delete(Request $request, $id)
{
$category = $this->em->getRepository('AppBundle:Category')->find($id);
$this->persistAndFlush($category);
return $this->redirect('category_index');
}
/**
* #param Request $request
* #param Category $category
* #return array|RedirectResponse
*/
public function handleForm(Request $request, $category)
{
$form = $this->formFactory->create(CategoryType::class, $category);
return $this->handleBaseForm($request, $form, $category, "category_index");
}
}
Just if needed, here are my services.yaml file
services:
app.category.manager:
class: AppBundle\Manager\CategoryManager
arguments: [ '#doctrine.orm.entity_manager', '#form.factory', '#router' ]
fos_user.doctrine_registry:
alias: doctrine
Hope it may help solving your issue, and get a cleaner code. Please, note that this is a personnal solution, and there are probably better solutions. But this one is a working one, which seems, for me, cleaner than just let the controller have all the logic
I have a Doctrine entity and I use JMS serializer to render it in my API.
I'd like to add a boolean field like this :
/**
* #var bool
*
* #ORM\Column(name = "is_serialized", type = "boolean")
*/
protected $isSerialized = true;
I also use an EventSubscriber to add some data to my entity before serialization.
I'd like to dynamically include or not each entity, based on the $isSerialized value (I can't modify the Doctrine Query).
class SerializationEventSubscriber extends EventSubscriberInterface
{
/**
* #param ObjectEvent $event
*/
public function onPostSerialize(ObjectEvent $event)
{
if (!$this->isGroup('api', $event)) {
return;
}
$entity = $event->getObject();
$visitor = $event->getVisitor();
if (!$object->isSerialized()) {
// Skip the current object and remove it from serialization
}
}
}
I can't find any information about this, neither in the JMS annotation documentation.
Here is my EventListener, but instead of removing the object I just skip nulled field.
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityManagerInterface;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\EventDispatcher\ObjectEvent;
use JMS\Serializer\Metadata\ClassMetadata as JMSClassMetadata;
use JMS\Serializer\Metadata\StaticPropertyMetadata;
class EntitySerializerListener
{
/**
* #var EntityManagerInterface
*/
protected $em;
public function __construct(EntityManagerInterface $entityManager)
{
$this->em = $entityManager;
}
public function onPostSerialize(ObjectEvent $event)
{
/** #var JsonSerializationVisitor $visitor */
$object = $event->getObject();
$visitor = $event->getVisitor();
$context = $event->getContext();
$type = $event->getType();
/** #var JMSClassMetadata $metadata */
$metadata = $context->getMetadataFactory()->getMetadataForClass($type['name']);
$data = $visitor->endVisitingObject($metadata, $object, $type);
$visitor->startVisitingObject($metadata, $object, $type);
// Here I remove unnecessary fields
$this->prune($type['name'], $data);
// Reset fresh serialized data
foreach ($data as $field => $value) {
$visitor->visitProperty(new StaticPropertyMetadata($type['name'], $field, $value), $value);
}
}
/**
* Prune the empty field which was set to NULL by MaxDepth annotation but left in the data graph by JWT serializer.
*
* #param string $fqcn
* #param array $data
*/
protected function prune(string $fqcn, array & $data)
{
/** #var ClassMetadata $metadata */
$metadata = $this->em->getMetadataFactory()->getMetadataFor($fqcn);
// Handle association
$associations = $metadata->getAssociationMappings();
foreach ($associations as $field => $association) {
if (!array_key_exists($field, $data)) {
continue;
}
// Here remove entity or any other field which you want
if (empty($data[$field])) {
unset($data[$field]);
} else {
$this->prune($association['targetEntity'], $data[$field]);
}
}
}
}
I need to get maximum ID of a table inside of symfony 2.7 entity. But instead of having the id I'm getting this issue.
Notice: Undefined property: AppBundle\Entity\BlogPost::$container
This is my BlogPost entity,
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* BlogPost
*
* #ORM\Table()
* #ORM\Entity
*/
class BlogPost {
const SERVER_PATH_TO_IMAGE_FOLDER = '/uploads';
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="body", type="text")
*/
private $body;
/**
* #var string
*
* #ORM\Column(name="filename", type="text")
*/
private $filename;
/**
* Set filename
*
* #param string $filename
* #return BlogPost
*/
public function setFilename($filename) {
$this->filename = $filename;
return $this;
}
public function setUploader(UploadedFile $file) {
$em = $this->container->get('doctrine.orm.entity_manager');
$highest_id = $em->createQueryBuilder()
->select('MAX(b.id)')
->from('AppBundle:BlogPost', 'b')
->getQuery()
->getSingleScalarResult();
var_dump($highest_id);
exit();// exit for check value
$url = 'uploads/events';
$file_name = 'fsdf.' . $file->guessExtension();
$file->move($url, $file_name);
}
/**
* Get filename
*
* #return string
*/
public function getFilename() {
return $this->filename;
}
/**
* #var boolean
*
* #ORM\Column(name="draft", type="boolean")
*/
private $draft;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return BlogPost
*/
public function setTitle($title) {
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle() {
return $this->title;
}
/**
* Set body
*
* #param string $body
* #return BlogPost
*/
public function setBody($body) {
$this->body = $body;
return $this;
}
/**
* Get body
*
* #return string
*/
public function getBody() {
return $this->body;
}
/**
* Set draft
*
* #param boolean $draft
* #return BlogPost
*/
public function setDraft($draft) {
$this->draft = $draft;
return $this;
}
/**
* Get draft
*
* #return boolean
*/
public function getDraft() {
return $this->draft;
}
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="blogPosts")
*/
private $category;
public function setCategory(Category $category) {
$this->category = $category;
}
public function getCategory() {
return $this->category;
}
/**
* Unmapped property to handle file uploads
*/
public $file;
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null) {
$this->file = $file;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile() {
return $this->file;
}
/**
* Manages the copying of the file to the relevant place on the server
*/
public function upload() {
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
// we use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and target filename as params
$this->getFile()->move(
self::SERVER_PATH_TO_IMAGE_FOLDER, $this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
$this->filename = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
$this->setFile(null);
}
/**
* Lifecycle callback to upload the file to the server
*/
public function lifecycleFileUpload() {
$this->upload();
}
/**
* Updates the hash value to force the preUpdate and postUpdate events to fire
*/
public function refreshUpdated() {
// $this->setUpdated(new \DateTime());
}
// ... the rest of your class lives under here, including the generated fields
// such as filename and updated
}
This is the part I'm trying to get max id,
public function setUploader(UploadedFile $file) {
$em = $this->container->get('doctrine.orm.entity_manager');
$highest_id = $em->createQueryBuilder()
->select('MAX(b.id)')
->from('AppBundle:BlogPost', 'b')
->getQuery()
->getSingleScalarResult();
var_dump($highest_id);
exit();// exit for check value
$url = 'uploads/events';
$file_name = 'fsdf.' . $file->guessExtension();
$file->move($url, $file_name);
}
With the help of comments and few research I figured out the issue is in 'entity_manager' part. Is there a way to call doctrine queries inside Entity?
If I were you, i would do something like that :
Controller:
$blogPost= new BlogPost () ;
$em = $this->getDoctrine()->getManager();
//..your code
// I assume you want to do that after a form post
$blogPost = $form->getData();
$id = $em->getRepository('AppBundle:BlogPost')->getMaxId();
$blogPost->setUploader($id);
//...
Repository:
public function getMaxId()
{
$qb = $this->createQueryBuilder('u');
$qb->select('u, MAX(id) as idMax');
return $qb->getQuery()->getSingleResult();
}
Entity:
public function setUploader(UploadedFile $file, $id)
{
var_dump($id);
$url = 'uploads/events';
$file_name = 'fsdf.'.$id.$file->guessExtension();
$file->move($url, $file_name);
}
It should work
I would suggest to hold the EntityManager as a class variable inside your Entity and either instantiate it via the constructor or via a setter-method which you call before you use the setUploader function.
This should be the cleanest and best readable solution.
Form another thread I found this and it's working for me.
global $kernel;
if ( 'AppCache' == get_class($kernel) )
{
$kernel = $kernel->getKernel();
}
$em = $kernel->getContainer()->get( 'doctrine.orm.entity_manager');
I can't use it in controller cause I have an admin class instead of controller on this. Since lot of people suggesting this using entity_manager inside entity is not good practice I'm using the code inside admin class instead of entity.
This is the original thread..
How to use entityManager inside Entity?
Try this in your Controller:
$blogPost= new BlogPost () ;
$em = $this->getDoctrine()->getManager();
$blogPost = $form->getData();
$maxId = $em->getRepository('AppBundle:BlogPost')->createQueryBuilder('b')
->where("MAX(b.id) as maxId")
->getQuery()
->getSingleResult();