symfony catch block doesnt stop error propagation - symfony

I have these entities
/**
* nmarea
* #ORM\Entity
* #UniqueEntity(fields={"area"}, message="error.nmArea.area.unique")
* #ORM\Table(name="master.nmarea")
* #Tenant
*/
class nmArea
{
/**
* #var int
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
**/
private $id;
/**
* #var string
* #Assert\NotBlank(message="error.nmArea.area.blank")
* #ORM\Column(name="area", type="string", length=100, nullable=false)
**/
private $area;
/**
* #var boolean
*
* #ORM\Column(name="eliminado", type="boolean", nullable=false)
**/
private $eliminado;
/**
* #ORM\ManyToOne(targetEntity="nmEmpresa")
* #ORM\JoinColumn(name="id_empresa", referencedColumnName="id", nullable=false)
*/
private $empresa;
/**
* #ORM\ManyToOne(targetEntity="nmUsuario")
* #ORM\JoinColumn(name="id_representante", referencedColumnName="id", nullable=false)
* #Assert\NotBlank(message="error.nmArea.representante.blank")
*/
private $representante;
/**
* #return mixed
*/
public function getEmpresa()
{
return $this->empresa;
}
/**
* #param mixed $empresa
*/
public function setEmpresa($empresa)
{
$this->empresa = $empresa;
}
/**
* #return mixed
*/
public function getRepresentante()
{
return $this->representante;
}
/**
* #param mixed $representante
*/
public function setRepresentante($representante)
{
$this->representante = $representante;
}
/**
* #return string
*/
public function getArea()
{
return $this->area;
}
/**
* #param string $area
*/
public function setArea($area)
{
$this->area = $area;
}
/**
* #return boolean
*/
public function getEliminado()
{
return $this->eliminado;
}
/**
* #param boolean $eliminado
*/
public function setEliminado($eliminado)
{
$this->eliminado = $eliminado;
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
public function getName()
{
return $this->area;
}
}
and
/**
* nmentidad_area
* #ORM\Entity
* #ORM\Table(name="master.nmentidad_area")
* #Tenant
*/
class nmEntidadArea
{
/**
* #var int
*
* #ORM\Column(name="id_entidad_area", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
**/
private $id;
/**
* #var boolean
*
* #ORM\Column(name="eliminado", type="boolean")
**/
private $eliminado;
/**
* #ORM\ManyToOne(targetEntity="nmEmpresa")
* #ORM\JoinColumn(name="id_empresa", referencedColumnName="id")
*/
private $empresa;
/**
* #ORM\ManyToOne(targetEntity="nmEntidad", cascade={"persist"})
* #ORM\JoinColumn(name="id_entidad", referencedColumnName="id")
*/
private $entidad;
/**
* #ORM\ManyToOne(targetEntity="nmArea")
* #ORM\JoinColumn(name="id_area", referencedColumnName="id")
*/
private $area;
/**
* #return mixed
*/
public function getArea()
{
return $this->area;
}
/**
* #param mixed $area
*/
public function setArea($area)
{
$this->area = $area;
}
/**
* #return mixed
*/
public function getEntidad()
{
return $this->entidad;
}
/**
* #param mixed $entidad
*/
public function setEntidad($entidad)
{
$this->entidad = $entidad;
}
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* #return mixed
*/
public function getEmpresa()
{
return $this->empresa;
}
/**
* #param mixed $empresa
*/
public function setEmpresa($empresa)
{
$this->empresa = $empresa;
}
/**
* #return boolean
*/
public function getEliminado()
{
return $this->eliminado;
}
/**
* #param boolean $eliminado
*/
public function setEliminado($eliminado)
{
$this->eliminado = $eliminado;
}
public function getName()
{
return $this->entidad->getNombre() . $this->area->getArea();
}
}
When I want to delete an nmArea instance I use this service
namespace AplicacionBaseBundle\DependencyInjection\Helpers;
use AplicacionBaseBundle\Entity\nmEntidad;
use AplicacionBaseBundle\Entity\nmEntidadArea;
use AplicacionBaseBundle\Entity\nmArea;
use Doctrine\DBAL\ConnectionException;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\PDOException;
use Doctrine\DBAL\Exception\ConstraintViolationException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\DBAL\Connection;
class SpamEntityHelper implements ContainerAwareInterface
{
use ContainerAwareTrait;
//... other code
public function deleteEntitySpam($repository, $id, $trueDelete = false, $hasRelations = false)
{
$em = $this->container->get('doctrine')->getManager();
$em->getConnection()->beginTransaction();
$em->getConnection()->setTransactionIsolation(Connection::TRANSACTION_SERIALIZABLE);
try {
$spamManager = $this->container->get('doctrine')->getRepository($repository);
$spam = $spamManager->find($id);
if ($trueDelete == false) {
$spam->setEliminado(true);
if (!$hasRelations) {
$cloned = clone $spam;
$em->remove($spam);
$em->flush();
$em->persist($cloned);
$em->flush();
}
} else {
$em->remove($spam);
$em->flush();
}
$em->getConnection()->commit();
return true;
} catch (DBALException $e) {
$em->getConnection()->rollBack();
$em->clear();
$this->container->get('doctrine')->resetEntityManager();
return false;
}
}
Thing is when I try to delete a nmArea object without removing it from nmEntidadArea first, Doctrine throws a ForeignKeyConstraintViolationException right in the commit line of the above service, and that is good: you cant delete if stills referenced, of course. What I cant figure out is why that error cant be catched with the try/catch block, nor like DBALException nor ForeignKeyConstraintViolationException: it doesnt matter how many catch I put, there is no way to deal with it.
I was thinking on subscribing to kernel.exception, but then I break the flow of the method and $em->getConnection()->rollBack(); throws that there is no active transaction
The full trace of error is this
exception […]
0 PDOException
message SQLSTATE[23503]: Foreign key violation: 7 ERROR: update o delete en «nmarea» viola la llave foránea «const_fk_nmentidadarea_representante» en la tabla «nmentidad_area» DETAIL: La llave (id)=(17) todavía es referida desde la tabla «nmentidad_area».
class PDOException
trace […]
0 {…}
namespace
short_class
class
type
function
file D:\xmp\htdocs\protosoft\vendor\doctrine\dbal\lib\Doctrine\DBAL\Connection.php
line 1289
args []
1 PDO
namespace
short_class PDO
class PDO
type ->
function commit
file D:\xmp\htdocs\protosoft\vendor\doctrine\dbal\lib\Doctrine\DBAL\Connection.php
line 1289
args []
2 Doctrine\DBAL\Connection
namespace Doctrine\DBAL
short_class Connection
class Doctrine\DBAL\Connection
type ->
function commit
file D:\xmp\htdocs\protosoft\src\AplicacionBaseBundle\DependencyInjection\Helpers\SpamEntityHelper.php
line 103
args []
3 AplicacionBaseBundle\DependencyInjection\Helpers\SpamEntityHelper
namespace AplicacionBaseBundle\DependencyInjection\Helpers
short_class SpamEntityHelper
class AplicacionBaseBundle\DependencyInjection\Helpers\SpamEntityHelper
type ->
function deleteEntitySpam
file D:\xmp\htdocs\protosoft\src\AplicacionBaseBundle\Controller\NomenclatorsController.php
line 45
args […]
4 AplicacionBaseBundle\Controller\NomenclatorsController
namespace AplicacionBaseBundle\Controller
short_class NomenclatorsController
class AplicacionBaseBundle\Controller\NomenclatorsController
type ->
function deleteENTITYAction
file D:\xmp\htdocs\protosoft\src\AplicacionBaseBundle\Controller\NomenclatorsController.php
line 82
args […]
5 AplicacionBaseBundle\Controller\NomenclatorsController
namespace AplicacionBaseBundle\Controller
short_class NomenclatorsController
class AplicacionBaseBundle\Controller\NomenclatorsController
type ->
function performDeleteAction
file D:\xmp\htdocs\protosoft\src\AplicacionBaseBundle\Controller\AreaController.php
line 58
args […]
6 AplicacionBaseBundle\Controller\AreaController
namespace AplicacionBaseBundle\Controller
short_class AreaController
class AplicacionBaseBundle\Controller\AreaController
type ->
function deleteAreaAction
file D:\xmp\htdocs\protosoft\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\HttpKernel.php
line 151
args […]
7 Symfony\Component\HttpKernel\HttpKernel
namespace Symfony\Component\HttpKernel
short_class HttpKernel
class Symfony\Component\HttpKernel\HttpKernel
type ->
function handleRaw
file D:\xmp\htdocs\protosoft\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\HttpKernel.php
line 68
args […]
8 Symfony\Component\HttpKernel\HttpKernel
namespace Symfony\Component\HttpKernel
short_class HttpKernel
class Symfony\Component\HttpKernel\HttpKernel
type ->
function handle
file D:\xmp\htdocs\protosoft\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\Kernel.php
line 169
args […]
9 Symfony\Component\HttpKernel\Kernel
namespace Symfony\Component\HttpKernel
short_class Kernel
class Symfony\Component\HttpKernel\Kernel
type ->
function handle
file D:\xmp\htdocs\protosoft\web\app_dev.php
line 29
UPDATE 1
After trying( again) the solutions provided in the answers without result I began to believe that it was not a properly said 'doctrine or symfony problem'. So i ran the delete query on my pgadmin(I m using postgresql) and the query result was exactly the same that in my app, literally the same:
update o delete en «nmarea» viola la llave foránea «const_fk_nmentidadarea_representante» en la tabla «nmentidad_area»
DETAIL: La llave (id)=(17) todavía es referida desde la tabla «nmentidad_area».
So, what I think is that when I try to delete Symfony and/or Doctrine look for errors and everything is Ok, but then the database returns the error, and for some reason the server does not handle that error and ends up being a fatal error, which is handled as a kernel error. But why?

You’re getting a PDOException, while you’re catch is limited to DBALException – which is not an instance of PDOException! So, obviously, the exception doesn’t get caught.
If you want to catch both types of exceptions, you can use the newly introduced catch pipe operator:
try
{
// …
}
catch (PDOException | DBALException $e)
{
// …
}
This works in PHP 7.1 and higher. If your code needs to run on servers with PHP < 7.1, you could listen for just any exception and then determine the type of exception within the catch block:
try
{
// …
}
catch (Exception $e)
{
if ($e instanceof PDOException || $e instanceof DBALException)
{
// …
}
else
{
throw $e;
}
}
Or you could use multiple catch blocks:
try
{
// …
}
catch (PDOException $e)
{
// …
}
catch (DBALException $e)
{
// …
}
Either way, remember to import namespaces properly, otherwise exceptions will not be caught.

Related

Symfony 4 with Doctrine - save ManyToOne

I have problem with my symfony code:
My repository update method
/**
* #param MenuModel $menu
* #return MenuEntity|MenuModel
* #throws RepositoryException
*/
public function updateMenu(MenuModel $menu)
{
try {
$transformedMenu = $this->menuTransformer->transform($menu);
$transformedMenu = $this->getEntityManager()->merge($transformedMenu);
$this->getEntityManager()->flush($transformedMenu);
$this->getEntityManager()->detach($transformedMenu);
return $this->menuTransformer->transform($transformedMenu);
} catch (\Exception $e) {
throw new RepositoryException($e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
}
}
My Element entity:
/**
* Element.
*
* #ORM\HasLifecycleCallbacks
* #ORM\Table(name="element")
* #ORM\ChangeTrackingPolicy("DEFERRED_EXPLICIT")
* #ORM\Entity(repositoryClass="CP\API\Repository\ElementRepository")
*/
class Element extends \CP\RestBundle\Model\Element
{
use Traits\SystemObjectTrait;
use Traits\ChannelsTrait;
use Traits\PropertiesTrait;
use Traits\WorkflowTrait;
/**
* #ORM\ManyToOne(targetEntity="Menu", inversedBy="contents")
* #ORM\JoinColumn(name="menu_id", referencedColumnName="id")
*/
protected $menu;
/**
* Element constructor.
*/
public function __construct()
{
parent::__construct();
}
/**
* #param int $id
*
* #return Element
*/
public function setId(int $id): self
{
$this->id = $id;
return $this;
}
/**
* Update publication by modification when empty
* Update status according to publication, unpublication and archiving
*
* #ORM\PrePersist()
*/
protected function prePersist()
{
$this->updatePublication();
$this->updateStatus();
}
/**
* Update publication by modification when empty
* Update status according to publication, unpublication and archiving
*
* #ORM\PreUpdate()
*/
public function preUpdate()
{
$this->updatePublication();
$this->updateStatus();
}
/**
* Increases object version
*/
public function increaseVersion()
{
++$this->version;
}
/**
* #return mixed
*/
public function getMenu()
{
return $this->menu;
}
/**
* #param mixed $menu
*/
public function setMenu($menu): void
{
$this->menu = $menu;
}
}
My Menu entity
<?php
namespace CP\API\Entity;
use CP\API\Entity\Traits\TimestampableTrait;
use CP\Model\Configuration;
use CP\Model\Content;
use CP\Model\Language;
use CP\Model\MenuTranslation;
use CP\RestBundle\Model\Locator;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\HasLifecycleCallbacks()
* #ORM\Table(name="custom_menu")
* #ORM\Entity(repositoryClass="CP\API\Repository\MenuRepository")
*/
class Menu
{
use TimestampableTrait;
/**
* #var int
*
* #ORM\Id()
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="CP\API\Entity\Element",mappedBy="menu",cascade={"persist"})
*/
protected $contents;
public function __construct(Menu $parent = null)
{
$dateTime = new \DateTime();
$this->creation = $dateTime;
$this->modification === null && $this->setModification($dateTime);
$this->contents = new ArrayCollection();
}
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* #return ArrayCollection
*/
public function getContents(): ArrayCollection
{
return $this->contents;
}
/**
* #param ArrayCollection $contents
*/
public function setContents(ArrayCollection $contents): void
{
$this->contents = $contents;
}
/**
* #param Element $element
* #return $this
*/
public function addContent(Element $element): self
{
if (!$this->contents->contains($element)) {
$this->contents[] = $element;
$element->setMenu($this);
}
return $this;
}
/**
* #param Element $element
* #return $this
*/
public function removeContent(Element $element): self
{
if ($this->contents->contains($element)) {
$this->contents->removeElement($element);
if ($element->getMenu() === $this) {
$element->setMenu(null);
}
}
return $this;
}
}
My menu model
<?php
namespace CP\Model;
use CP\RestBundle\Model\Element;
use CP\RestBundle\Model\Locator;
use CP\RestBundle\Model\Product;
use CP\RestBundle\Model\Traits\TimestampableTrait;
class Menu extends BaseModel
{
/** #var Content[] */
protected $content;
/**
* #return array|null
*/
public function getContent(): ?array
{
return $this->content;
}
/**
* #param Content[] $content
*/
public function setContent(array $content): void
{
$this->content = $content;
}
}
My transformer from model to entity
public function transform($object)
{
if ($object instanceof Menu) {
$menuData = new MenuEntity();
if ($object->getId())
$menuData->setId($object->getId());
if ($object->getContent() instanceof Content) {
$contentEntity = new ContentEntity();
$content = $object->getContent();
$contentEntity->setId($content->getId());
$menuData->addContent($this->attachToEntityManager($contentEntity));
}
return $menuData;
}
private function attachToEntityManager($object)
{
try {
$attachedObject = $this->entityManager->merge($object);
return $attachedObject;
} catch (ORMException $e) {
throw new ModelTransformationException(sprintf('Model transformation error, object could not be attached to Entity Manager: %s in %s',
$e->getMessage(), $e->getFile() . ':' . $e->getLine()));
}
}
When i try saved data then i don't have any error, but on database nothing change i.e. element entity don't have assign menu_id from relation. I don't know what is wrong, maybe i don't know how to use and save OneToMany relation.
Any idea?
Can you give us more context of what your code is doing ? The constructor of MenuEntity is strange, no use of $parent, a boolean comparaison isn't used. Why do you need to use detach and merge methods of entityManager
- Mcsky
Is right, I don't see the point of using detach and/or merge here.
Normally with a OneToMany relationship you use entity manager and persist both relations, flush and you should have an entity with a OneToMany relationship.
I hope this might be helpful, specifically this part: https://symfony.com/doc/current/doctrine/associations.html#saving-related-entities
example:
// relates this product to the category
$product->setCategory($category);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($category);
$entityManager->persist($product);
$entityManager->flush();

Symfony rest api

I'm developing a rest api application with symfony in backend.
My problem is that when I try to lauch a GET request, the application runs without giving any response even after even after some ten minutes.
this is the request method:
/**
* CounterRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class CounterRepository extends \Doctrine\ORM\EntityRepository
{
/**
* #param string $name
* #param int $date
* #return mixed
* #throws \Exception
*/
public function getValuesByNameAndDate(string $name, int $date)
{
$query = $this
->createQueryBuilder('c')
->leftJoin('c.values', 'v', 'WITH')
->addSelect('v')
->where('c.name = :name')
->andWhere('v.date = :date')
->setParameters(
array(
'date' => $date,
'name' => $name
)
)
->getQuery();
return $query->getResult();
}
}
The CounterValue Entity:
/**
* CounterValue
*
* #ORM\Table(name="counter_value")
* #ORM\Entity(repositoryClass="AppBundle\Repository\CounterValueRepository")
*/
class CounterValue
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var float
*
* #ORM\Column(name="value", type="float")
*/
private $value;
/**
* #var int
*
* #ORM\Column(name="date", type="integer")
*/
private $date;
/**
* #var Counter
*
* #ORM\ManyToOne(targetEntity="Counter", inversedBy="values")
* #ORM\JoinColumn()
*/
private $counter;
/**
* #var City
*
* #ORM\ManyToOne(targetEntity="City", inversedBy="values"))
* #ORM\JoinColumn()
*/
protected $city;
public function __construct()
{
$date = new \DateTime('-1 day');
$this->date = strtotime($date->format('d-m-Y'));
}
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set value.
*
* #param float $value
*
* #return CounterValue
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* Get value.
*
* #return float
*/
public function getValue()
{
return $this->value;
}
/**
* Set date.
*
* #param int $date
*
* #return CounterValue
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date.
*
* #return int
*/
public function getDate()
{
return $this->date;
}
/**
* #return Counter
*/
public function getCounter(): Counter
{
return $this->counter;
}
/**
* #param Counter $counter
*/
public function setCounter(Counter $counter): void
{
$this->counter = $counter;
}
/**
* #return City
*/
public function getCity(): City
{
return $this->city;
}
/**
* #param City $city
*/
public function setCity(City $city): void
{
$this->city = $city;
}
}
/**
* Counter
*
* #ORM\Table(name="counter")
* #ORM\Entity(repositoryClass="AppBundle\Repository\CounterRepository")
*/
class Counter
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=false)
*/
private $name;
/**
* #var XmlFile
*
* #ORM\ManyToOne(targetEntity="XmlFile", inversedBy="counters", cascade={"remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $xmlFile;
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="CounterValue", mappedBy="counter", cascade={"persist"})
*/
private $values;
public function __construct()
{
$this->values = new ArrayCollection();
}
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name.
*
* #param string $name
*
* #return Counter
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name.
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set xmlFile
*
* #param XmlFile $xmlFile
*/
public function setXmlFile(XmlFile $xmlFile)
{
$this->xmlFile = $xmlFile;
}
/**
* Get xmlFile
*
* #return XmlFile
*/
public function getXmlFile(): XmlFile
{
return $this->xmlFile;
}
/**
* #return ArrayCollection
*/
public function getValues(): ArrayCollection
{
return $this->values;
}
/**
* #param ArrayCollection $values
*/
public function setValues(ArrayCollection $values): void
{
$this->values = $values;
}
/**
* Add $value
*
* #param CounterValue $value
*/
public function addValue(CounterValue $value)
{
$value->setCounter($this);
if(!$this->values->contains($value)){
$this->values->add($value);
}
}
}
And finally my method in controller
$em = $this->get('doctrine.orm.entity_manager');
$counterRep = $em->getRepository('AppBundle:Counter');
return $counterRep->getValuesByNameAndDate('L.IRATHO.E2G.CSFB.ExecSuccOut', 1533600000);
Just return a Json response like this.
public function listAction(Request $request)
{
try
{
$em = $this->get('doctrine.orm.entity_manager');
$counterRep = $em->getRepository('AppBundle:Counter')->getValuesByNameAndDate('L.IRATHO.E2G.CSFB.ExecSuccOut', 1533600000);
return new JsonResponse(['success' => true, 'code' => Response::HTTP_OK, 'data' => $counterRep]);
}
catch (\Exception $exception)
{
return new JsonResponse(['success' => false,'code' => $exception->getCode() ,'message' => $exception->getMessage()]);
}
}
Controller action should return instance of
Symfony\Component\HttpFoundation\Response
You try to return result of query builder $query->getResult()
You need to change it to something like
public function counterValuesAction(Request $request) {
try {
$em = $this->get('doctrine.orm.entity_manager');
$counterRep = $em->getRepository('AppBundle:Counter');
$values = $counterRep->getValuesByNameAndDate('L.IRATHO.E2G.CSFB.ExecSuccOut', 1533600000);
} catch (\Exception $e) {
return new Response($values);
}
}
Solution for Symfony 4+:
<?php
namespace App\Controller;
class CounterController extends AbstractController
{
public function listAction(
CounterRepository $repository // DI works like this in Controllers...
) {
$result = $repository->getValuesByNameAndDate('L.IRATHO.E2G.CSFB.ExecSuccOut', 1533600000);
// use JsonResponse object to create JSON response
return $this->json($result, Response::HTTP_OK);
}
}
// routes.yaml
counter_list:
path: /counter
controller: App\Controller\CounterController::listAction
methods: GET
Clear cache and try to send GET request to http://your-dev-host/counter
How does it work?
Controller
You need to create controller CounterController class with action function listAction. It is good practice to add Action postfix to action function.
You can define services or other DI objects as parameters in action functions. DI will set it. For example:
class MyController extends AbstractController
{
public function listAction(
Request $request,
SerializerInterface $serializer,
ValidatorInterface $validator,
CounterRepository $repository
) {
// ...
}
}
In order to send good response with JSON and all headers that is needed you need to use AbstractController::json function. It creates JsonResponse object. You can check it implementation.
More information about symfony controllers you can find in this article https://symfony.com/doc/current/controller.html
Routing
Next step is to create route for your new controller action. You can do it in 2 ways:
Adding record to config/routes.yaml file as i did
Use annotations (Annotations is bad practice because comments should not break programm)
More information about routing you can find in this article https://symfony.com/doc/current/routing.html
It does not work for me! How can I debug it?
First, dont panic and read error message. Also, you can check if routing works well. You can do it using command
$ bin/console debug:router
----------------------- -------- -------- ------ -------------------------------------
Name Method Scheme Host Path
----------------------- -------- -------- ------ -------------------------------------
_preview_error ANY ANY ANY /_error/{code}.{_format}
register_token POST ANY ANY /auth/register
refresh_token POST ANY ANY /auth/refresh
auth_login POST ANY ANY /auth/login
restroom_list GET ANY ANY /api/v1/restroom
----------------------- -------- -------- ------ -------------------------------------
In table you need to check your new url and how Symfony sees it.
Second, try to send request to this URL using curl and check response.
$ curl http://localhost/auth/login

Value must be type of string, null returned

Have the following Entity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
/**
* Address
*
* #ApiResource(
* collectionOperations={"get"={"method"="GET"}},
* itemOperations={"get"={"method"="GET"}}
* )
* #ORM\Table(name="address")
* #ORM\Entity
*/
class Address
{
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #return string
*/
public function getLat(): string
{
return $this->lat;
}
/**
* #param string $lat
*/
public function setLat(string $lat): void
{
$this->lat = $lat;
}
/**
* #return string
*/
public function getLng(): string
{
return $this->lng;
}
/**
* #param string $lng
*/
public function setLng(string $lng): void
{
$this->lng = $lng;
}
/**
* #return string
*/
public function getStreet(): string
{
return $this->street;
}
/**
* #param string $street
*/
public function setStreet(string $street): void
{
$this->street = $street;
}
/**
* #return string
*/
public function getZipcode(): string
{
return $this->zipcode;
}
/**
* #param string $zipcode
*/
public function setZipcode(string $zipcode): void
{
$this->zipcode = $zipcode;
}
/**
* #return string
*/
public function getCity(): string
{
return $this->city;
}
/**
* #param string $city
*/
public function setCity(string $city): void
{
$this->city = $city;
}
/**
* #return string
*/
public function getDescription(): string
{
return $this->description;
}
/**
* #param string $description
*/
public function setDescription(string $description): void
{
$this->description = $description;
}
/**</h2>
* #var string
*
* #ORM\Column(name="lat", type="decimal", precision=10, scale=8, nullable=false)
*/
private $lat;
/**
* #var string
*
* #ORM\Column(name="lng", type="decimal", precision=10, scale=8, nullable=false)
*/
private $lng;
/**
* #var string
*
* #ORM\Column(name="street", type="string", length=255, nullable=false)
*/
private $street;
/**
* #var string
*
* #ORM\Column(name="zipcode", type="string", length=5, nullable=false)
*/
private $zipcode;
/**
* #var string
*
* #ORM\Column(name="city", type="string", length=255, nullable=false)
*/
private $city;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=false)
*/
private $description;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
}
The table with one row of data:
I got this error:
"type": "https://www.rfc-editor.org/rfc/rfc2616#section-10",
"title": "An error occurred",
"detail": "Type error: Return value of App\Entity\Address::getLat() must be of the type string, null returned",
Where is my fault? Using Symfony 4.0.
The getter getLat has the return type hint string, which means only actual string values (that also means: no null values!) as return values are accepted.
You didn't show the code where you actually work with the entity, but it basically comes down to the default value of every property in an object being null if not defined differently.
Look at this example:
$address = new Address();
// the following lines will produce an error in your example
// because your return type hint doesn't allow null values
$address->getId(); // returns null
$address->getStreet(); // returns null
$address->getLat(); // returns null
$address->setLat("4.56789");
$address->getLat(); // returns "4.56789"
Note regarding Doctrine:
If the values in the database are set correctly, you won't run into this problem after Doctrine populated the entity (e.g. via $address = $addressRepo->find(123);). It should only happen, when you create a new entity yourself and then try to use the getter methods.
Possible solutions:
1.) Allow null values as return values. Prepend your return type hints with a question mark, like this:
/**
* #return string|null
*/
public function getLat(): ?string
{
return $this->lat;
}
But if you do this, your code must be ready to handle null values from these methods!
2.) Define default values with the correct data type in your object:
/**
* #var string
*
* #ORM\Column(name="lat", type="decimal", precision=10, scale=8, nullable=false)
*/
private $lat = "";
Your code must be ready to handle empty strings as return values then! Alternativly you can also define the default values in a constructor method.
3.) Require these properties to be available by making them parameters of your constructor:
public function __constructor(string $lat, string $lng /*, add the other required properties */) {
$this->lat = $lat;
$this->lng = $lng;
// ... additional properties here ...
}
In that case you must provide the values when you create the object with new Address(/* parameters go here */);.
Symfony 5:
public function getSomething(): ?string
{
return $this->something;
}
public function setSomething(string $something): self
{
$this->something= $something;
return $this;
}
just remove string from (string $something) like this and it should work, it does for me
public function getSomething(): ?string
{
return $this->something;
}
public function setSomething($something): self
{
$this->something= $something;
return $this;
}

Symfony2 fixtures

I have this error when trying to use d:f:l on Smyony2, i got the same error for Servicio and Usuario, so I post one example and if you help me, the other one will be fixed too.
[ErrorException]
Catchable Fatal Error: Argument 1 passed to PGE\BitacoraBundle\Entity\Tarea::setServicio() must be an instance of PGE\BitacoraBundle\Entity\Servicio, integer given, called in D:\Zend\Apache2\htdocs\PGE\src\PGE\BitacoraBu
ndle\DataFixtures\ORM\Tareas.php on line 20 and defined in D:\Zend\Apache2\htdocs\PGE\src\PGE\BitacoraBundle\Entity\Tarea.php line 281
This is the code:
Tareas.php
<?php
namespace PGE\BitacoraBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use PGE\BitacoraBundle\Entity\Tarea;
class Tareas extends AbstractFixture implements OrderedFixtureInterface
{
public function getOrder()
{
return 3;
}
public function load(ObjectManager $manager)
{
for ($i = 1; $i < 5; $i++) {
$entidad = new Tarea();
$entidad->setServicio($i);
$entidad->setFechaInicio(new \DateTime());
$entidad->setFechaFinal(new \DateTime());
$entidad->setDescripcion('Descripcion aqui');
$entidad->setEstado('Pendiente');
$entidad->setPrioridad('P1');
$entidad->setResumen('Hola soy un resumen');
$entidad->setSolicitante('x068753');
$entidad->setUsuario($i);
}
$manager->flush();
}
}
Usuario.php
<?php
namespace PGE\BitacoraBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="bitacora_usuario")
*/
class Usuario
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/** #ORM\Column(type="string", length=255) */
private $nombre;
/** #ORM\Column(type="string", length=255) */
private $apellidos;
/** #ORM\Column(type="string", length=255) */
private $prbes;
public function __toString()
{
return $this->getNombre().' '.$this->getApellidos();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set nombre
*
* #param string $nombre
* #return Usuario
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
return $this;
}
/**
* Get nombre
*
* #return string
*/
public function getNombre()
{
return $this->nombre;
}
/**
* Set apellidos
*
* #param string $apellidos
* #return Usuario
*/
public function setApellidos($apellidos)
{
$this->apellidos = $apellidos;
return $this;
}
/**
* Get apellidos
*
* #return string
*/
public function getApellidos()
{
return $this->apellidos;
}
/**
* Set prbes
*
* #param string $prbes
* #return Usuario
*/
public function setPrbes($prbes)
{
$this->prbes = $prbes;
return $this;
}
/**
* Get prbes
*
* #return string
*/
public function getPrbes()
{
return $this->prbes;
}
}
EDIT: I'm using Symfony 2.1.X
There is no problem with the bundle, instead it says very clearly that the first argument passed to $entidad->setServicio($i); must be an instance of PGE\BitacoraBundle\Entity\Servicio and not an integer.
So you have ot get an instance of Serivcio and pass this to the setter. If you create the servicio inside another fixture, you need to look into the sharing objects between fixtures section of the bundle documentation.

Symfony 2 - Retrieve Entity with Json, return another Entity

I have a problem while json_encodeing a Entity.
public function jsonvoteAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('KorumAGBundle:AGVote')->findOneById($id);
$response = new Response(json_encode($entity, 200));
$response->headers->set('Content-Type',' application/json');
return $response;
}
This code returns me a the users entity
{"users":{"__isInitialized__":false,"id":null,"nickname":null,"pwd":null,"email":null,"firstname":null,"lastname":null,"poste":null,"addr1":null,"addr2":null,"pc":null,"country":null,"phone":null,"province":null,"acess":null,"site":null,"crew":null,"utilisateur":null}}
And when I var dymp my $entity, it returns both my AGVote and USers entity.
Here is my AGVote Entity
<?php
namespace Korum\AGBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Korum\AGBundle\Entity\AGVote
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class AGVote
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $question;
/**
* #ORM\Column(type="smallint")
*/
private $actif;
/**
* #ORM\ManyToOne(targetEntity="\Korum\KBundle\Entity\Users", cascade={"all"})
*/
public $users;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set question
* Nb : Only AG admin can set a question
* #param text $question
*/
public function setQuestion($question)
{
$this->question = $question;
}
/**
* Get question
*
* #return text
*/
public function getquestion()
{
return $this->question;
}
/**
* Set actif
*
* #param smallint $actif
*/
public function setActif($actif)
{
$this->actif = $actif;
}
/**
* Get actif
*
* #return smallint
*/
public function getActif()
{
return $this->actif;
}
/**
* Set Users
*
* #param Korum\KBundle\Entity\Province $Users
*/
public function setUsers(\Korum\KBundle\Entity\Users $users)
{
$this->users = $users;
}
/**
* Get Users
*
* #return Korum\KBundle\Entity\Users
*/
public function getUsers()
{
return $this->users;
}
}
Does anyone have an idea of what happened ?
I tried to install the JSMSerializerBundle but event with Metadata library at version 1.1.
When I want to clear my cache, it failed with error :
See :
JMSSerializerBundle Installation : Catchable Fatal Error: Argument 1 passed to JMSSerializerBundle\Twig\SerializerExtension::__construct()
By default, json_encode only uses public properties.
So it serialized the only public property of AGVote: $users. The content of $users was an instance of User; which public fields were serialized.
You could work around these by adding a toArray() method to your entities, and then doing json_encode($entity->toArray()), but i highly recommend you to have a look and use the JMSSerializedBundle.

Resources