API Platform Sonata Media Bundle Gallery - Circular Reference - symfony

New in SF3, I use API Platform and Sonata Media Bundle.
I'm blocked while getting Gallery entity of Sonata using API Platform GET request.
"A circular reference has been detected when serializing the object of class \"Application\\Sonata\\MediaBundle\\Entity\\Gallery\" (configured limit: 1)"
The admin of the entity works great, I can add a gallery to the entity.
When the entity have a gallery it cause this error, when it does not it's ok.
Entity Technic
GET /technics in API Platform
[
{
"id": 0,
"type": "string",
"comment": "string",
"links": [
"string"
],
"gallery": "string"
}
]
Entity Class
<?php
// src/AppBundle/Entity/Technic.php
namespace AppBundle\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* #ORM\Entity
* #ApiResource
*/
class Technic
{
/**
* #var int The id of this evaluation.
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #var string $type TechnicType of the evaluation
*
* #ORM\OneToOne(targetEntity="TechnicType")
* #Assert\NotBlank
*/
public $type;
/**
* #var string $note Note of the evaluation
*
* #ORM\Column(type="string", length=255, nullable=true)
*/
public $comment;
/**
* #var Link[] Link Links of this technic.
*
* #ORM\ManyToMany(targetEntity="Link", cascade={"persist"})
*/
private $links;
/**
* #ORM\OneToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Gallery",cascade={"persist"})
* #ORM\JoinColumn(name="gallery", referencedColumnName="id", nullable=true)
*/
private $gallery;
/**
* Constructor
*/
public function __construct()
{
$this->links = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set type
*
* #param \AppBundle\Entity\TechnicType $type
*
* #return Technic
*/
public function setType(\AppBundle\Entity\TechnicType $type = null)
{
$this->type = $type;
return $this;
}
/**
* Get type
*
* #return \AppBundle\Entity\TechnicType
*/
public function getType()
{
return $this->type;
}
/**
* Add link
*
* #param \AppBundle\Entity\Link $link
*
* #return Technic
*/
public function addLink(\AppBundle\Entity\Link $link)
{
$this->links[] = $link;
return $this;
}
/**
* Remove link
*
* #param \AppBundle\Entity\Link $link
*/
public function removeLink(\AppBundle\Entity\Link $link)
{
$this->links->removeElement($link);
}
/**
* Get links
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getLinks()
{
return $this->links;
}
/**
* Set comment
*
* #param string $comment
*
* #return Technic
*/
public function setComment($comment)
{
$this->comment = $comment;
return $this;
}
/**
* Get comment
*
* #return string
*/
public function getComment()
{
return $this->comment;
}
/**
* Set gallery
*
* #param \Application\Sonata\MediaBundle\Entity\Gallery $gallery
*
* #return Technic
*/
public function setGallery(\Application\Sonata\MediaBundle\Entity\Gallery $gallery = null)
{
$this->gallery = $gallery;
return $this;
}
/**
* Get gallery
*
* #return \Application\Sonata\MediaBundle\Entity\Gallery
*/
public function getGallery()
{
return $this->gallery;
}
}
Thank a lot guys, I'm desesperate I try a lot of things in StackQ/A, annotations, seraliazer config...

You need to configure serialization correctly. Either setup serialization groups, so that on GETting some entity serializer would only pick (for example) IDs of related entities, or set up circualr reference handler in normalizer and inject this normalizer into serializer.
$normalizer = new GetSetMethodNormalizer();
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
There might be more specific answer for api-platform, which I don't know, because serialization of related entities is popular issue.

Related

Symfony and Doctrine databases

I am working on some new project and the project is nearly done using Symfony framework, but the problem that i am used to CodeIgnitor Framework and basically as a Java developer/Android a lot of stuff i got confused with when working on Web development so here is the situation:
The website have a user end and an admin end (i am working on the Admin end), so there are these tables in the database which i really don't understand why they are built like this but this is not the problem
what i would like to know is how to add a service_category field with the corresponding translations in the service_category_translation using forms or any other way
this is the ServiceCategory Entity
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use JMS\Serializer\Annotation as Serializer;
/**
* class ServiceCategory
*
* #ORM\Table(name="service_category")
* #ORM\Entity
*
* #Serializer\ExclusionPolicy("all")
*/
class ServiceCategory
{
use ORMBehaviors\Timestampable\Timestampable;
use ORMBehaviors\SoftDeletable\SoftDeletable;
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Serializer\Expose
* #Serializer\Groups({"general-information", "service-offer"})
*/
private $id;
/**
* #var ServiceGroup
*
* #ORM\OneToMany(targetEntity="ServiceGroup", mappedBy="serviceCategory")
*
* #Serializer\Expose
* #Serializer\Groups({"service-offer"})
*/
private $serviceGroup;
/**
* Constructor
*/
public function __construct()
{
$this->serviceGroup = new ArrayCollection();
}
/**
* {#inheritdoc}
*/
public function __toString()
{
return $this->getName() ? $this->getName() : '';
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Get translated name
*
* #return string
*
* #Serializer\VirtualProperty
* #Serializer\SerializedName("name")
* #Serializer\Groups({"invoice-list", "service-offer"})
*/
public function getName()
{
if($this->getTranslations()->get($this->getCurrentLocale()) == null){
return 'sorry';
}
return $this->getTranslations()->get($this->getCurrentLocale())->getName();
}
/**
* Add serviceGroup
*
* #param ServiceGroup $serviceGroup
*
* #return ServiceCategory
*/
public function addServiceGroup(ServiceGroup $serviceGroup)
{
$this->serviceGroup[] = $serviceGroup;
return $this;
}
/**
* Remove serviceGroup
*
* #param ServiceGroup $serviceGroup
*/
public function removeServiceGroup(ServiceGroup $serviceGroup)
{
$this->serviceGroup->removeElement($serviceGroup);
}
/**
* Get serviceGroup
*
* #return Collection
*/
public function getServiceGroup()
{
return $this->serviceGroup;
}
}
and this is the ServiceCategoryTranslation Entity
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Class ServiceCategoryTranslation
*
* #package CoreBundle\Entity
*
* #ORM\Entity
* #ORM\Table(name="service_category_translation")
*/
class ServiceCategoryTranslation
{
use ORMBehaviors\Translatable\Translation;
use ORMBehaviors\Timestampable\Timestampable;
use ORMBehaviors\SoftDeletable\SoftDeletable;
/**
* #ORM\Column(type="string", length=255)
*/
protected $name;
/**
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #param string
* #return null
*/
public function setName($name)
{
$this->name = $name;
}
public function __toString() {
return $this->name;
}
}
how can i achieve this ?
please don't guide me to symfony or doctrine documentation i have been lost there for two days now and i am running late on the schedule
Thanks in advance
You have a one-to-many-association from ServiceCategory (1) to ServiceCategoryTranslations (many) since I assume you
will manage the transaltions from the category. This have to be a bidirectional association, have a look
here
You have to add a property to manage the entities and describe the association. I will do it with annotations.
use Doctrine\Common\Collections\ArrayCollection;
class ServiceCategory
{
/**
* #OneToMany(targetEntity="ServiceCategoryTranslation", mappedBy="serviceCategory")
**/
private $translations;
public function __construct()
{
$this->translations = new ArrayCollection();
}
/**
* #return ServiceCategoryTranslation[]
*/
public function getStandort(){
return $this->translations;
}
/**
* #param ArrayCollection $translations
* #return ServiceCategory
*/
public function setTranslations(ArrayCollection $translations)
{
$this->translations->clear();
foreach ($translations as $translation){
$this->addTranslation($translation);
}
return $this;
}
/**
* #param ServiceCategoryTranslation $translation
* #return ServiceCategory
*/
public function addTranslation(ServiceCategoryTranslation $translation){
/* this is a way to keep the integerity */
$translation->setServiceCategory($this);
if(!$this->translation){
$this->translations = new ArrayCollection();
}
$this->translations->add($translation);
return $this;
}
/**
* #param ServiceCategoryTranslation $translation
* #return ServiceCategory
*/
public function removeStandort(ServiceCategoryTranslation $translation){
$this->translations->removeElement($translation);
return $this;
}
}
class ServiceCategoryTranslation
{
/**
* #ManyToOne(targetEntity="ServiceCategory", inversedBy="translations")
* #JoinColumn(name="translatable_id", referencedColumnName="id")
**/
private $serviceCategory;
/**
* #param ServiceCategoryTranslation $translation
* #return ServiceCategoryTranslation
*/
public function setServiceCategory(ServiceCategory $serviceCategory){
$this->serviceCategory = $serviceCategory;
return $this;
}
/* getter analog */
}

How to search ManyToOne side by OneToMany

I have Four Entities:
Batch
BachGroup
BachGroupApplication
Application
Entity 1(Batch) has OneToMany Bidirectional relationship with Entity 2(BatchGroup).
Entity 2(BatchGroup) has OneToMany Bidirectional relationship with Entity 3(BachGroupApplication).
Entity 3(BachGroupApplication) has ManyToOne Bidirectional relationship with Entity 4(Application).
DETAIL ON ABOVE ENTITIES:
Each batch has it's groups which i'm storing in bachGroup entity with batch reference column in it, now each batchGroup can have multiple applications so i have created BatchGroupApplication entity which holds reference to batchGroup and Application.
TODO:
I want to be able to search applications by individual batch group which i'm not able to do i searched the web and stackoverflow but failed to get it working! i don't know if my mind has stopped somewhere! my entities are listed below please help:
Batch Entity
<?php
namespace ModelBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Batch
*
* #ORM\Table(name="batch")
* #ORM\Entity(repositoryClass="ModelBundle\Repository\BatchRepository")
*/
class Batch
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ..............
/**
* If a Batch is removed all it's associated Groups will be deleted as well..
*
* #ORM\OneToMany(targetEntity="BatchGroup", mappedBy="batch", cascade={"remove"})
**/
private $batchGroups;
public function __construct() {
$this->batchGroups = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
// ...............
/**
* Add batchGroups
*
* #param \ModelBundle\Entity\BatchGroup $batchGroups
* #return Batch
*/
public function addBatchGroup(\ModelBundle\Entity\BatchGroup $batchGroups)
{
$this->batchGroups[] = $batchGroups;
return $this;
}
/**
* Remove batchGroups
*
* #param \ModelBundle\Entity\BatchGroup $batchGroups
*/
public function removeBatchGroup(\ModelBundle\Entity\BatchGroup $batchGroups)
{
$this->batchGroups->removeElement($batchGroups);
}
/**
* Get batchGroups
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getBatchGroups()
{
return $this->batchGroups;
}
}
BatchGroup Entity
<?php
namespace ModelBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* BatchGroup
*
* #ORM\Table(name="batch_group")
* #ORM\Entity()
*/
class BatchGroup
{
/**
* #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=128)
*/
private $title;
/**
* #ORM\ManyToOne(targetEntity="Batch", inversedBy="batchGroups")
* #ORM\JoinColumn(name="batch_id", referencedColumnName="id")
**/
private $batch;
/**
* #ORM\OneToMany(targetEntity="BatchGroupApplication", mappedBy="batchGroup", cascade={"remove"})
**/
private $batchGroupApplications;
public function __construct() {
$this->batchGroupApplications = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
// ............
/**
* Set batch
*
* #param \ModelBundle\Entity\Batch $batch
* #return BatchGroup
*/
public function setBatch(\ModelBundle\Entity\Batch $batch = null)
{
$this->batch = $batch;
return $this;
}
/**
* Get batch
*
* #return \ModelBundle\Entity\Batch
*/
public function getBatch()
{
return $this->batch;
}
/**
* Add batchGroupApplications
*
* #param \ModelBundle\Entity\BatchGroupApplication $batchGroupApplications
* #return BatchGroup
*/
public function addBatchGroupApplication(\ModelBundle\Entity\BatchGroupApplication $batchGroupApplications)
{
$this->batchGroupApplications[] = $batchGroupApplications;
return $this;
}
/**
* Remove batchGroupApplications
*
* #param \ModelBundle\Entity\BatchGroupApplication $batchGroupApplications
*/
public function removeBatchGroupApplication(\ModelBundle\Entity\BatchGroupApplication $batchGroupApplications)
{
$this->batchGroupApplications->removeElement($batchGroupApplications);
}
/**
* Get batchGroupApplications
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getBatchGroupApplications()
{
return $this->batchGroupApplications;
}
}
BatchGroupApplication Entity
<?php
namespace ModelBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* BatchGroupApplication
*
* Table generated through this entity class will hold all application and can be filtered through BatchGroup
*
* #ORM\Table(name="batch_group_application")
* #ORM\Entity()
*/
class BatchGroupApplication
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="BatchGroup", inversedBy="batchGroupApplications")
* #ORM\JoinColumn(name="batch_group_id", referencedColumnName="id")
**/
private $batchGroup;
/**
* #ORM\ManyToOne(targetEntity="Application", inversedBy="batchGroupApplications")
* #ORM\JoinColumn(name="application_id", referencedColumnName="id")
**/
private $application;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set batchGroup
*
* #param \ModelBundle\Entity\BatchGroup $batchGroup
* #return BatchGroupApplication
*/
public function setBatchGroup(\ModelBundle\Entity\BatchGroup $batchGroup = null)
{
$this->batchGroup = $batchGroup;
return $this;
}
/**
* Get batchGroup
*
* #return \ModelBundle\Entity\BatchGroup
*/
public function getBatchGroup()
{
return $this->batchGroup;
}
/**
* Set application
*
* #param \ModelBundle\Entity\Application $application
* #return BatchGroupApplication
*/
public function setApplication(\ModelBundle\Entity\Application $application = null)
{
$this->application = $application;
return $this;
}
/**
* Get application
*
* #return \ModelBundle\Entity\Application
*/
public function getApplication()
{
return $this->application;
}
}
Application Entity
<?php
namespace ModelBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Application
*
* #ORM\Table(name="application")
* #ORM\Entity(repositoryClass="ModelBundle\Repository\ApplicationRepository")
*/
class Application
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ............
/**
* #ORM\OneToMany(targetEntity="BatchGroupApplication", mappedBy="application", cascade={"remove"})
**/
private $batchGroupApplications;
public function __construct()
{
$this->batchGroupApplications = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
// ............
/**
* Add batchGroupApplications
*
* #param \ModelBundle\Entity\BatchGroupApplication $batchGroupApplications
* #return Application
*/
public function addBatchGroupApplication(\ModelBundle\Entity\BatchGroupApplication $batchGroupApplications)
{
$this->batchGroupApplications[] = $batchGroupApplications;
return $this;
}
/**
* Remove batchGroupApplications
*
* #param \ModelBundle\Entity\BatchGroupApplication $batchGroupApplications
*/
public function removeBatchGroupApplication(\ModelBundle\Entity\BatchGroupApplication $batchGroupApplications)
{
$this->batchGroupApplications->removeElement($batchGroupApplications);
}
/**
* Get batchGroupApplications
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getBatchGroupApplications()
{
return $this->batchGroupApplications;
}
}
ApplicationRepository
Search function in this repository is being called from controller and from data is being sent to it via parameter that is in key value pair format, because form may have more properties so i'm checking if user is searching through batchGroup as well if yes it should combine the result from there as well.
namespace ModelBundle\Repository;
use Doctrine\ORM\EntityRepository;
/**
* ApplicationRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ApplicationRepository extends EntityRepository
{
/**
* Search Applications
*
* data: search form data in key => value fashion
*
*
*/
public function search($data)
{
$em = $this->getEntityManager();
$applicationRepository = $em->getRepository('ModelBundle:Application');
$qb = $applicationRepository->createQueryBuilder('a');
foreach ($data as $field => $value) {
if($field=="batchGroupApplications"){
$qb->andWhere(":value_$field MEMBER OF a.batchGroupApplications")->setParameter("value_$field", $value);
}else{
$qb->andWhere("a.$field = :value_$field")->setParameter("value_$field", $value);
}
}
$query = $qb->getQuery();
$result = $query->getResult();
return $result;
}
}
Controller
Here i'm creating a search form with batchGroupApplications dropdown, where user will choose any batch group and submit the form, once submitted user should be able to see all applications which are in that group but i'm getting nothing!
namespace SISBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use ModelBundle\Entity\Batch;
use SISBundle\Form\BatchType;
use ModelBundle\Entity\Application;
use DefaultBundle\Form\ApplicationType;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use SISBundle\Form\SearchApplicationType;
use ModelBundle\Entity\Group;
use ModelBundle\Entity\BatchGroup;
/**
* Admission controller.
*
* #Route("/admission")
*/
class AdmissionController extends Controller
{
// ..............
/**
* List Applications for selected Batch
*
* #Route("/{batch}/applications")
*
* #Method({"GET", "POST"})
* #Template()
*/
public function applicationsAction(Request $request, Batch $batch)
{
$em = $this->getDoctrine()->getManager();
if( $batch != null ){
$applications = $batch->getApplications();
/*
* Search Applications
*
*/
$searchForm = $this->createForm(SearchApplicationType::class, null, array(
'action' => $this->generateUrl('sis_admission_applications', array('batch'=>$batch->getId())),
'method' => 'POST',
'attr' => array(
'novalidate' => 'novalidate',
'id' => 'sis_application_search_form',
),
));
// Manually Populating groups dropdown based on their availability for current loaded batch
$searchForm->add('batchGroupApplications', ChoiceType::class, array(
'choices' => $batch->getBatchGroups(),
'choices_as_values' => true,
'multiple'=>false,
'choice_label'=>'title',
'placeholder'=>' - Select Group..',
)
);
$searchForm->handleRequest($request);
if ($searchForm->isSubmitted()) {
// data is an array with "firstname", "middlename", and "lastname" keys etc
$data = $searchForm->getData();
$applications = $em->getRepository('ModelBundle:Application')->search($data);
}
/*********************************** end: Search Applications ***************************************/
return array(
'batch' => $batch,
'applications' => $applications,
'searchForm' => $searchForm->createView(),
);
}
}
}
Again, please suggest how can i search applications through batchGroup in andWhere fashion as search form may have some more fields which are available in application entity, for other fields it's working fine but this search by BatchGroup is somewhat tricky may be because of complicated associations etc.
Looking forward to solution..

Knp DoctrineBehaviors throws ContextErrorException: Warning: call_user_func_array() expects parameter 1 to be a valid callback

I use symfony2.4 and KNP doctrine behaviors translatable.
I have entity Site (for ID, host, enabled) and entity SiteTranslation (for translated fields: name, descriptions, …).
I use query to get results
$qb = $this->createQueryBuilder('s')
->addSelect('translation') // to eager fetch translations (optional)
->leftJoin('s.translations', 'translation') // or innerJoin ?
->orderBy('s.root', 'ASC')
->addOrderBy('s.lft', 'ASC');
I would like to print result in Twig. For ID, host and enabled fields from Site entity it's easy:
{{ item.id }}
But I can't print translated fields (name, description, …)
{{ item.name }}
It doesn't work.
Error message:
ContextErrorException: Warning: call_user_func_array() expects parameter 1 to be a valid >callback, class 'Net\ConBundle\Entity\SiteTranslation' does not have a method 'name' in >D:\Users...\vendor\knplabs\doctrine->behaviors\src\Knp\DoctrineBehaviors\Model\Translatable\TranslatableMethods.php line 140
Getters and setters for translatable fields are in SiteTranslation entity.
UPDATE:
I still didn't find a solution for an error.
Here is Site entity:
<?php
namespace Pnet\ConlocoBundle\Entity;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* #UniqueEntity("host", message="site.host.unique", groups={"edit"})
* #Gedmo\Tree(type="nested")
* #ORM\Entity(repositoryClass="Pnet\ConlocoBundle\Entity\Repository\SiteRepository")
* #ORM\Table(name="site")
*/
class Site
{
use ORMBehaviors\Translatable\Translatable; // knp translatable strategy
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=40, unique=true)
* #Assert\NotBlank(message="site.host.notBlank", groups={"edit"})
* #Assert\Length(max = "40", maxMessage = "site.host.maxLength", groups={"edit"})
*/
protected $host;
/**
* #ORM\Column(type="string", length=255, nullable=true)
* #Assert\Image()
*/
protected $image;
/**
* #ORM\Column(type="boolean")
* #Assert\Choice(choices = {"1", "0"}, message = "site.isDefault.choice", groups={"edit"})
*/
protected $isDefault;
/**
* #ORM\Column(type="boolean")
* #Assert\Choice(choices = {"1", "0"}, message = "site.enabled.choice", groups={"edit"})
*/
protected $enabled;
/**
* #ORM\Column(type="string", length=64, nullable=true)
*/
protected $analytics;
/**
* #Gedmo\TreeLeft
* #ORM\Column(name="lft", type="integer")
*/
private $lft;
/**
* #Gedmo\TreeLevel
* #ORM\Column(name="lvl", type="integer")
*/
private $lvl;
/**
* #Gedmo\TreeRight
* #ORM\Column(name="rgt", type="integer")
*/
private $rgt;
/**
* #Gedmo\TreeRoot
* #ORM\Column(name="root", type="integer", nullable=true)
*/
private $root;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Site", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Site", mappedBy="parent")
* #ORM\OrderBy({"lft" = "ASC"})
*/
private $children;
private $file;
public $idByFilter;
public $nameByFilter;
/**
* Proxy translations (Knp/Doctrine Behaviors)
* An extra feature allows you to proxy translated fields of a translatable entity.
* You can use it in the magic __call method of you translatable entity so that when
* you try to call getName (for example) it will return you the translated value
* of the name for current locale:
*/
public function __call($method, $arguments)
{
return $this->proxyCurrentLocaleTranslation($method, $arguments);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set host
*
* #param string $host
* #return Site
*/
public function setHost($host)
{
$this->host = $host;
return $this;
}
/**
* Get host
*
* #return string
*/
public function getHost()
{
return $this->host;
}
/**
* Set isDefault
*
* #param boolean $isDefault
* #return Site
*/
public function setIsDefault($isDefault)
{
$this->isDefault = $isDefault;
return $this;
}
/**
* Get isDefault
*
* #return boolean
*/
public function getIsDefault()
{
return $this->isDefault;
}
/**
* Set enabled
*
* #param boolean $enabled
* #return Site
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
return $this;
}
/**
* Get enabled
*
* #return boolean
*/
public function getEnabled()
{
return $this->enabled;
}
/**
* Set analytics
*
* #param string $analytics
* #return Site
*/
public function setAnalytics($analytics)
{
$this->analytics = $analytics;
return $this;
}
/**
* Get analytics
*
* #return string
*/
public function getAnalytics()
{
return $this->analytics;
}
/**
* Get ID from Filter
*
* #return string
*/
public function getIdByFilter()
{
return $this->idByFilter;
}
/**
* Get name from Filter
*
* #return string
*/
public function getNameByFilter()
{
return $this->nameByFilter;
}
/**
* Set image
*
* #param string $image
* #return Site
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set file
*
* #param string $file
* #return Site
*/
public function setFile($file)
{
$this->file = $file;
return $this;
}
/**
* Get file
*
* #return string
*/
public function getFile()
{
return $this->file;
}
/**
*
* Tree functions
*/
public function setParent(Site $parent = null)
{
$this->parent = $parent;
}
public function getParent()
{
return $this->parent;
}
public function getRoot()
{
return $this->root;
}
public function getLvl()
{
return $this->lvl;
}
public function getChildren()
{
return $this->children;
}
public function getLft()
{
return $this->lft;
}
public function getRgt()
{
return $this->rgt;
}
/**
* Add a method to the entity class that shows the name indented by nesting level
*/
public function getLeveledName()
{
return str_repeat(
html_entity_decode(' ', ENT_QUOTES, 'UTF-8'),
($this->getLvl()) * 3
) . $this->getName();
}
public function getLeveledPosition()
{
return str_repeat(
html_entity_decode(' ', ENT_QUOTES, 'UTF-8'),
($this->getLvl()) * 3
);
}
}
And here is SiteTranslation entity:
namespace Pnet\ConlocoBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* #ORM\Entity
*/
class SiteTranslation
{
use ORMBehaviors\Translatable\Translation;
/**
* #ORM\Column(type="string", length=60)
* #Assert\NotBlank(message="site.name.notBlank", groups={"edit"})
* #Assert\Length(max = "60", maxMessage = "site.name.maxLength", groups={"edit"})
*/
protected $name;
/**
* #ORM\Column(type="string", length=100)
* #Assert\NotBlank(message="site.title.notBlank", groups={"edit"})
* #Assert\Length(max = "100", maxMessage = "site.title.maxLength", groups={"edit"})
*/
protected $title;
/**
* #ORM\Column(type="string", length=200)
* #Assert\NotBlank(message="site.longTitle.notBlank", groups={"edit"})
* #Assert\Length(max = "200", maxMessage = "site.longTitle.maxLength", groups={"edit"})
*/
protected $longTitle;
/**
* #ORM\Column(type="string", length=250, nullable=true)
* #Assert\Length(max = "250", maxMessage = "site.keywords.maxLength", groups={"edit"})
*/
protected $keywords;
/**
* #ORM\Column(type="string", length=500, nullable=true)
* #Assert\Length(max = "500", maxMessage = "site.description.maxLength", groups={"edit"})
*/
protected $description;
/**
* Set name
*
* #param string $name
* #return Site
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set title
*
* #param string $title
* #return Site
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set longTitle
*
* #param string $longTitle
* #return Site
*/
public function setLongTitle($longTitle)
{
$this->longTitle = $longTitle;
return $this;
}
/**
* Get longTitle
*
* #return string
*/
public function getLongTitle()
{
return $this->longTitle;
}
/**
* Set keywords
*
* #param string $keywords
* #return Site
*/
public function setKeywords($keywords)
{
$this->keywords = $keywords;
return $this;
}
/**
* Get keywords
*
* #return string
*/
public function getKeywords()
{
return $this->keywords;
}
/**
* Set description
*
* #param string $description
* #return Site
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
}
TL;DR:
As a (probably dirty) workaround, use
{{ item.getName }}
in your Twig template instead of
{{ item.name }}
Explanation:
I came across the same issue and i think that this should be considered a bug in the Knp DoctrineBehaviors documentation when used with Twig. When you call this in your Twig template :
{{ item.name }}
This is what Twig does behind the scenes to get the name property :
try to get the name public property of the item object
if not found, checks for the name public method of the item object
if not found, checks for the getName() public method of the "item" object
if not found, checks for the __call() magic method (and calls it with the name parameter)
The problem here is step 4. The magic __call() method that you defined (as recommended by the official DoctrineBehaviors documentation) is called with the name parameter instead of getName. It then calls the proxyCurrentLocaleTranslation() method who tries to call the name public method of your translation class. Of course, it doesn't exist because you only have a getName() method.
See this issue in Twig : https://github.com/twigphp/Twig/issues/342
By using directly the {{ item.getName }} code in Twig, the proper method name will be called.
This work for me:
public function __call($method, $arguments)
{
try {
return $this->proxyCurrentLocaleTranslation($method, $arguments);
} catch (\Symfony\Component\Debug\Exception\ContextErrorException $e) {
return $this->proxyCurrentLocaleTranslation('get' . ucfirst($method), $arguments);
}
}
You haven't moved all the translatable properties/methods to your <Name>Translation class.
The exception clearly states that there is no name/getName method in your SiteTranslation class.
Please read my answer over here to see how Knp\DoctrineBehaviors's magic translation proxy is used correctly.

Use doctrine in a personalized class

I have a Util class in a Symfony 2.4 proyect that get the slug from a book. The slug cannot be repeated. Each slug must be unique. I have the following code form the class that generate the slug. I have the error:
Fatal error: Using $this when not in object context in C:\xampp\htdocs\gens\src\
Sakya\GensBundle\Util\Util.php on line 38
namespace Sakya\GensBundle\Util;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\EntityManager;
class Util
{
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public static function getSlug($slug)
{
$separador = '-';
$tildes=array('á','é','í','ó','ú');
$vocales=array('a','e','i','o','u');
str_replace($vocales,$tildes,$slug);
$slug = iconv('UTF-8', 'ASCII//TRANSLIT', $slug);
$slug = preg_replace("/[^a-zA-Z0-9\/_|+ -]/", '', $slug);
$slug = strtolower(trim($slug, $separador));
$slug = preg_replace("/[\/_|+ -]+/", $separador, $slug);
$i = 1;
while (! $this->em->getRepository("GensBundle:Libro")->findBySlug($slug)){
$slug = $slug."-".$i;
$i++;
}
return $slug;
}
}
It is mi Entity "Libro"
<?php
namespace Sakya\GensBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Sakya\GensBundle\Util\Util;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Libro
*
* #ORM\Table()
* #ORM\Entity
*/
class Libro
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="libro", type="string", length=255)
*/
private $libro;
/**
* #ORM\Column(type="string")
*
* #Assert\NotBlank()
*/
protected $slug;
/**
* #var string
*
* #ORM\Column(name="autor", type="string", length=255)
*/
private $autor;
/**
* #var string
*
* #ORM\Column(name="prefacio", type="string", length=700)
*/
private $prefacio;
/**
* #ORM\OneToMany(targetEntity="Capitulo", mappedBy="libro")
*/
private $capitulo;
public function __toString()
{
return $this->getLibro();
}
/**
* Constructor
*/
public function __construct()
{
$this->capitulo = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set libro
*
* #param string $libro
* #return Libro
*/
public function setLibro($libro)
{
$this->libro = $libro;
$this->slug = Util::getSlug($libro);
return $this;
}
/**
* Get libro
*
* #return string
*/
public function getLibro()
{
return $this->libro;
}
/**
* Set autor
*
* #param string $autor
* #return Libro
*/
public function setAutor($autor)
{
$this->autor = $autor;
return $this;
}
/**
* Get autor
*
* #return string
*/
public function getAutor()
{
return $this->autor;
}
/**
* Add capitulo
*
* #param \Sakya\GensBundle\Entity\Capitulo $capitulo
* #return Libro
*/
public function addCapitulo(\Sakya\GensBundle\Entity\Capitulo $capitulo)
{
$this->capitulo[] = $capitulo;
return $this;
}
/**
* Remove capitulo
*
* #param \Sakya\GensBundle\Entity\Capitulo $capitulo
*/
public function removeCapitulo(\Sakya\GensBundle\Entity\Capitulo $capitulo)
{
$this->capitulo->removeElement($capitulo);
}
/**
* Get capitulo
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCapitulo()
{
return $this->capitulo;
}
/**
* Set prefacio
*
* #param string $prefacio
* #return Libro
*/
public function setPrefacio($prefacio)
{
$this->prefacio = $prefacio;
return $this;
}
/**
* Get prefacio
*
* #return string
*/
public function getPrefacio()
{
return $this->prefacio;
}
/**
* Set slug
*
* #param string $slug
* #return Libro
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
}
How can I make a consult that check that the slug is not in the database form the Uti Class?
Thanks for all and sorry for my English
The problem is you are trying refer to a property from static method. Don't do it unless the property is also static.
The best way I can suggest is change the static method into not-static, then create a service from Util class, and make validation outside the entity. It's not a good idea using service inside entity class.
You didn't show the context but I can guess: if you want set slug based on a form - then the best way to achieve what you want would be create a validator.

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