Doctrine2 One to many relation inverse side not saving owning side id - mysql

I have 2 entities: Submission and Vote.
Submission Entity:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Votes", mappedBy="submission",cascade={"persist", "remove" })
* #ORM\JoinColumn(name="id", referencedColumnName="submission_id")
*/
protected $vote;
/**
* #return mixed
*/
public function getVote()
{
return $this->vote->toArray();
}
/**
* #param Votes $vote
* #return $this
*
*/
public function setVote(Votes $vote)
{
if (!$this->vote->contains($vote)) {
$this->vote->add($vote);
}
return $this;
}
Vote entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Submission", inversedBy="id")
* #ORM\JoinColumn(name="submission_id", referencedColumnName="id", nullable=true)
*/
protected $submission;
/**
* #return mixed
*/
public function getSubmission()
{
return $this->submission;
}
/**
* #param mixed $submission
*/
public function setSubmission($submission)
{
$this->submission = $submission;
}
Problem is that when i'm setting vote for previously selected submission:
$submission = $em->getRepository('AppBundle:Submission')->findOneById($submissionId);
$vote = new Votes();
$vote->setId($submission->getId());
$vote->setFeedback($judgeComment);
$vote->setScore($judgeScore);
$vote->setSubmission($submissionId);
$submission->setVote($vote);
$em->persist($submission);
$em->flush();
Vote table
submission_id column is always NULL - i'm not sure what i'm doing wrong. I want to store here submission id, for what this vote is.

You don't have to set ids in doctrine but objects, as it is an ORM
$submission = $em->getRepository('AppBundle:Submission')->findOneById($submissionId);
$vote = new Votes();
$vote->setId($submission->getId()); // <--- Are you sure that you need this?
$vote->setFeedback($judgeComment);
$vote->setScore($judgeScore);
// $vote->setSubmission($submissionId); <--- ERROR!
$vote->setSubmission($submission) // <--- Check advice below
$submission->setVote($vote);
$em->persist($submission);
$em->flush();
If I can also leave a suggestion here, modify setVote as follows
public function setVote(Votes $vote)
{
if (!$this->vote->contains($vote)) {
$this->vote->add($vote);
$vote->setSubmission($this);
}
return $this;
}
and you'll never need to set explicitly both sides of association anymore
I'm also noticing that your class name is plural (should be singular into domain) and that vote attribute into submission is singular (should be plural as it's a collection; you have a -to-Many annotation. Even the setter should be an adder as you're not setting but adding a Vote)

Thank you very much Gerry. Thanks to you problem is solved. Here is the answer:
Submission Entity:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Votes", mappedBy="submission",cascade={"persist", "remove" })
* #ORM\JoinColumn(name="id", referencedColumnName="submission_id")
*/
protected $vote;
/**
* #return mixed
*/
public function getVote()
{
return $this->vote->toArray();
}
/**
* #param Votes $vote
* #return $this
*
*/
public function setVote(Votes $vote)
{
$this->vote[] = $vote;
$vote->setSubmission($this);
return $this;
}
Vote Entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Submission", inversedBy="vote")
* #ORM\JoinColumn(name="submission_id", referencedColumnName="id",onDelete="cascade", nullable=true)
*/
protected $submission;
/**
* #return mixed
*/
public function getSubmission()
{
return $this->submission;
}
/**
* #param mixed $submission
*/
public function setSubmission($submission)
{
$this->submission = $submission;
}
Now when i'm adding new vote, everything is fine:
$vote = new Votes();
$vote->setFeedback($judgeComment);
$vote->setScore($judgeScore);
$submission->setVote($vote);
$em->persist($submission);
$em->flush();
result

Related

how to serialize only one level deep of an entity with symfony serializer?

I would like to serialize my objects into text fields in order to store a representation of them for traceability.
What I really want is a JSON representation of the entity's properties, and whenever there is an object, I would like a JSON representation of that as well, but only on that first level, I don't want it to dig deeper into what possible objects and relations there is below that, I'm happy with the object ID's
Many objects reference other objects, and from this documentation https://symfony.com/doc/current/components/serializer.html#handling-circular-references it appears this can be easily handled by just storing the object's ID rather than serializing the entire object (again). But in my case it doesn't work =) Am I missing something critical here?
Entity
<?php
namespace App\Entity;
use App\Repository\RegularServiceHoursRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass=RegularServiceHoursRepository::class)
*/
class RegularServiceHours
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="datetime")
*/
private $open;
/**
* #ORM\Column(type="datetime")
*/
private $close;
/**
* #var object \App\Entity\ACRGroup
*
* #ORM\ManyToOne(targetEntity="\App\Entity\ACRGroup", inversedBy="regularServiceHours")
* #ORM\JoinColumn(name="acr_group", referencedColumnName="id", nullable=false)
*/
protected $ACRGroup;
public function getId(): ?int
{
return $this->id;
}
public function getOpen(): ?\DateTimeInterface
{
return $this->open;
}
public function setOpen(\DateTimeInterface $open): self
{
$this->open = $open;
return $this;
}
public function getClose(): ?\DateTimeInterface
{
return $this->close;
}
public function setClose(\DateTimeInterface $close): self
{
$this->close = $close;
return $this;
}
/**
* Set aCRGroup
*
* #param \App\Entity\ACRGroup $aCRGroup
*
* #return DebitPeriod
*/
public function setACRGroup(\App\Entity\ACRGroup $aCRGroup)
{
$this->ACRGroup = $aCRGroup;
return $this;
}
/**
* Get aCRGroup
*
* #return \App\Entity\ACRGroup
*/
public function getACRGroup()
{
return $this->ACRGroup;
}
/**
* Get debitTimeSeconds
*
* #return int
*/
public function getTimeSeconds()
{
$open = $this->getOpen();
$close = $this->getClose();
$r = $close->format('U') - $open->format('U');
return $r;
}
}
Controller
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Serializer;
public function log($type,$message,$unit=null,$previous=null,$current=null) {
//We only log successful operations.
//If you want to log also errors, be very careful to not have anything persisted already, in que to be flushed. The flush below will store possible entities under conflict check and entities that sent you here due to a failed validation/conflict check, entities that weren't meant to be saved but rejected and forgotten.
if ($type == 'success') {
$encoder = new JsonEncoder();
$defaultContext = [
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($object, $format, $context) {
return $object->getId();
},
];
$normalizer = new ObjectNormalizer(null, null, null, null, null, null, $defaultContext);
$serializer = new Serializer([$normalizer], [$encoder]);
dd($serializer->serialize($current, 'json'));
It never gets to the dump action, it chews for 30 seconds until memory is exhausted, and then it says:
Error: Maximum execution time of 30 seconds exceeded
Have I somehow missed where to set a default depth of 1? (I understood depth 1 to be the default when nothing was set).
Pay attention to Serialization Groups Attributes. With this attribute you can select the desired data when serializing an object.
Specify groups to the desired object properties:
class RegularServiceHours
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"default"})
*/
private $id;
/**
* #ORM\Column(type="datetime")
*/
private $open;
/**
* #ORM\Column(type="datetime")
* #Groups({"default"})
*/
private $close;
/**
* #var object \App\Entity\ACRGroup
*
* #ORM\ManyToOne(targetEntity="\App\Entity\ACRGroup", inversedBy="regularServiceHours")
* #ORM\JoinColumn(name="acr_group", referencedColumnName="id", nullable=false)
* #Groups({"default"})
*/
private $ACRGroup;
}
Also specify a group for the ACRGroup identifier
class ACRGroup
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"default"})
*/
private $id;
}
And just specify the group when serializing the object
$serializer->serialize($current, 'json', ['groups' => ['default']]);
I think that should solve your problem

Eloquent query optimization

I have below a query build with eloquent on laravel, it is very slow (50 points it take to show around 10 second), i can't understand if it a problem of the query construct or some missing index on table, some advise?
thanks
Points model (around 5000 records on the table)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use OwenIt\Auditing\Contracts\Auditable;
class Points extends Model implements Auditable
{
use \OwenIt\Auditing\Auditable;
use SoftDeletes;
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'points';
/**
* The database primary key value.
*
* #var string
*/
protected $primaryKey = 'id';
/**
* Attributes that should be mass-assignable.
*
* #var array
*/
protected $fillable = [
'schedule_id',
'customer_id',
'cart_zone_id',
'indirizzo',
'latitudine',
'longitudine',
'tipo',
'enabled'
];
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = [];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [];
/**
* Get the Schedule for this model.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function Schedule()
{
return $this->belongsTo('App\Models\Schedules', 'schedule_id', 'id')->withTrashed();
}
/**
* Get the Customer for this model.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function Customer()
{
return $this->belongsTo('App\Models\Customers', 'customer_id', 'id')->withTrashed();
}
/**
* Get the CartZone for this model.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function CartZone()
{
return $this->belongsTo('App\Models\CartZones', 'cart_zone_id', 'id')->withTrashed();
}
}
Events model (around 400k records on the table)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
class RecordEvents extends Model
{
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'events';
/**
* The database primary key value.
*
* #var string
*/
protected $primaryKey = 'id';
/**
* Attributes that should be mass-assignable.
*
* #var array
*/
protected $fillable = [
'code',
'description',
'device_id',
'schedule_id',
'boa_id',
'parent_id',
'user_id',
'skip_unique'
];
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = [];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [];
/**
* Get the device for this model.
*
* #return App\Models\Device
*/
public function device()
{
return $this->belongsTo('App\Models\Devices', 'device_id')->withTrashed();
}
/**
* Get the schedule for this model.
*
* #return App\Models\Schedule
*/
public function schedule()
{
return $this->belongsTo('App\Models\Schedules', 'schedule_id')->withTrashed();
}
/**
* Get the boa for this model.
*
* #return App\Models\Boa
*/
public function boa()
{
return $this->belongsTo('App\Models\Points', 'boa_id')->withTrashed();
}
/**
* Get the boa for this model.
*
* #return App\Models\Boa
*/
public function pvd()
{
return $this->belongsTo('App\Models\CustomersPvds', 'pvd_id')->withTrashed();
}
/**
* Get the boa for this model.
*
* #return App\Models\Boa
*/
public function cartzone()
{
return $this->belongsTo('App\Models\CartZones', 'cartzone_id')->withTrashed();
}
/**
* Get the parent for this model.
*
* #return App\Models\Parent
*/
public function parent()
{
return $this->belongsTo('App\Models\Parent', 'parent_id')->withTrashed();
}
/**
* Get the user for this model.
*
* #return App\Models\User
*/
public function user()
{
return $this->belongsTo('App\Models\Users', 'user_id')->withTrashed();
}
/**
* Get created_at in array format
*
* #param string $value
* #return array
*/
public function getCreatedAtAttribute($value)
{
return \DateTime::createFromFormat($this->getDateFormat(), $value)->format('d/m/Y H:i:s');
}
/**
* Get data in array format
*
* #param string $value
* #return string
*/
public function getDataRawAttribute($value)
{
return Carbon::createFromFormat('d/m/Y H:is', $this->created_at)->format('Y-m-d H:i:s');
}
}
Query
$pointsCartZone = Points::where('cart_zone_id', $trip->cart_zone_id)
->where('enabled', 1)
->select('*', DB::raw('(select COUNT(*) as ch from events where boa_id = points.id and schedule_id = ' . $id . ') as passed'))
->get();
try to put on events table the index on boa_id and schedule_id and on points table on cart_zone_id.
let me know if it works :-)

Doctrine 2 and vertical partitioning

I'm currently working on a project for which i have to deal with a lot of users fields (+/- 80). My first approach was (and still is) to vertically partition the user table in 4 tables : main informations, administrative informations, banking informations, statistics informations. The ids of the 3 extra tables rows refer to the auto incremented id of the main table.
My questions are :
Is it relevant to do so ?
It seems like, with the way i did the
association, Doctrine isn't able to flush one object into database
in one shot :
Entity of type MyVendor\User\UserBundle\Entity\UsersInfosBank is missing an
assigned ID for field 'id_user'. The identifier generation strategy
for this entity requires the ID field to be populated before
EntityManager#persist() is called. If you want automatically generated
identifiers instead you need to adjust the metadata mapping
accordingly.
The project will be pushed into production in 3 weeks, so we're still able to go back to a more traditionnal way to store these datas into one huge table…
****************UPDATE*****************
WHOLE CODE LOGIC BELOW*****************
I just removed most of the properties and methods which are not concerned by my problem…
And yes, i'm using FOSUserBundle ;)
UsersMain
<?php
namespace LCP\User\UserBundle\Entity;
use FOS\UserBundle\Model\GroupableInterface;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* UsersMain
*
* #ORM\Table(name="lcp_users_main")
* #ORM\Entity
*/
class UsersMain extends BaseUser
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*
* #ORM\OneToOne(targetEntity="LCP\User\UserBundle\Entity\UsersInfosBank", mappedBy="id_user", cascade={"persist"})
*/
private $infosBank;
/**
*
* #ORM\OneToOne(targetEntity="LCP\User\UserBundle\Entity\UsersApodis", mappedBy="id_user", cascade={"persist"})
*/
private $apodis;
/**
*
* #ORM\OneToOne(targetEntity="LCP\User\UserBundle\Entity\UsersInfosAdmin", mappedBy="id_user", cascade={"persist"})
*/
private $infosAdmin;
/**
*
* #ORM\OneToOne(targetEntity="LCP\User\UserBundle\Entity\UsersInfosStats", mappedBy="id_user", cascade={"persist"})
*/
private $infosStats;
/**
* Constructor
*/
public function __construct()
{
$this->groups = new \Doctrine\Common\Collections\ArrayCollection();
$this->creationDate = new \Datetime();
}
/**
* Set apodis
*
* #param \LCP\User\UserBundle\Entity\UsersApodis $apodis
* #return UsersMain
*/
public function setApodis(\LCP\User\UserBundle\Entity\UsersApodis $apodis = null)
{
if(is_null($this->apodis) && is_null($apodis)){
$this->apodis = new \LCP\User\UserBundle\Entity\UsersApodis();
} else {
$this->apodis = $infosBank;
}
$this->apodis->setIdUser($this);
return $this;
}
/**
* Get apodis
*
* #return \LCP\User\UserBundle\Entity\UsersApodis
*/
public function getApodis()
{
if(is_null($this->infosAdmin)){
$this->infosAdmin = new \LCP\User\UserBundle\Entity\UsersApodis();
}
return $this->apodis;
}
/**
* Set infosAdmin
*
* #param \LCP\User\UserBundle\Entity\UsersInfosAdmin $infosAdmin
* #return UsersMain
*/
public function setInfosAdmin(\LCP\User\UserBundle\Entity\UsersInfosAdmin $infosAdmin = null)
{
if(is_null($this->infosAdmin) && is_null($infosAdmin)){
$this->infosAdmin = new \LCP\User\UserBundle\Entity\UsersInfosAdmin();
} else {
$this->infosAdmin = $infosAdmin;
}
$this->infosAdmin->setIdUser($this);
return $this;
}
/**
* Get infosAdmin
*
* #return \LCP\User\UserBundle\Entity\UsersInfosAdmin
*/
public function getInfosAdmin()
{
if(is_null($this->infosAdmin)){
$this->infosAdmin = new \LCP\User\UserBundle\Entity\UsersInfosAdmin();
}
return $this->infosAdmin;
}
/**
* Set infosStats
*
* #param \LCP\User\UserBundle\Entity\UsersInfosStats $infosStats
* #return UsersMain
*/
public function setInfosStats(\LCP\User\UserBundle\Entity\UsersInfosStats $infosStats = null)
{
if(is_null($this->infosStats) && is_null($infosStats)){
$this->infosStats = new \LCP\User\UserBundle\Entity\UsersInfosStats();
} else {
$this->infosStats = $infosAdmin;
}
$this->infosStats->setIdUser($this);
return $this;
}
/**
* Get infosStats
*
* #return \LCP\User\UserBundle\Entity\UsersInfosStats
*/
public function getInfosStats()
{
if(is_null($this->infosStats)){
$this->infosStats = new \LCP\User\UserBundle\Entity\UsersInfosStats();
}
return $this->infosStats;
}
}
UsersInfosAdmin
<?php
namespace LCP\User\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* UsersInfosAdmin
*
* #ORM\Table(name="lcp_users_infos_admin", indexes={#ORM\Index(name="pharmacyCip_idx", columns={"pharmacy_cip"})})
* #ORM\Entity
*/
class UsersInfosAdmin
{
/**
* #var integer
* #ORM\Id
* #ORM\OneToOne(targetEntity="LCP\User\UserBundle\Entity\UsersMain", cascade={"persist"}, inversedBy="infosAdmin")
*/
private $id_user;
/**
* Set id_user
*
* #param \LCP\User\UserBundle\Entity\UsersMain $idUser
* #return UsersInfosAdmin
*/
public function setIdUser(\LCP\User\UserBundle\Entity\UsersMain $idUser)
{
$this->id_user = $idUser;
return $this;
}
/**
* Get id_user
*
* #return \LCP\User\UserBundle\Entity\UsersMain
*/
public function getIdUser()
{
return $this->id_user;
}
}
UsersInfosStats
<?php
namespace LCP\User\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* UsersInfosStats
*
* #ORM\Table(name="lcp_users_infos_stats")
* #ORM\Entity
*/
class UsersInfosStats
{
/**
* #var integer
* #ORM\Id
* #ORM\OneToOne(targetEntity="LCP\User\UserBundle\Entity\UsersMain", cascade={"persist"}, inversedBy="infosStats")
*/
private $id_user;
/**
* Set id_user
*
* #param \LCP\User\UserBundle\Entity\UsersMain $idUser
* #return UsersInfosStats
*/
public function setIdUser(\LCP\User\UserBundle\Entity\UsersMain $idUser)
{
$this->id_user = $idUser;
return $this;
}
/**
* Get id_user
*
* #return \LCP\User\UserBundle\Entity\UsersMain
*/
public function getIdUser()
{
return $this->id_user;
}
}
Here is how i create a user (it is through a secured admin area form) :
/**
* This action get the field from the form
*/
public function userSaveAction(Request $request)
{
$userManager = $this->get('fos_user.user_manager');
$this->request = $request;
$userValues = $this->getRequest()->request->all();
$this->em = $this->getDoctrine()->getManager();
$user = $this->processUser($userValues);
$userManager->updateUser($user, false);
return $this->redirect($this->generateUrl('lcp_admin_user_edit', ['id'=>$user->getId()]));
}
/**
* This method process user fields and uses setters (or getters for joined entities) to set datas of user
*/
private function processUser($userValues)
{
if($userValues['userId']=="") {
$user = $this->get('fos_user.user_manager')->createUser();
} else {
$user = $this->em->getRepository('LCPUserBundle:UsersMain')
->findOneBy(['id' => $userId]);
}
//construction of related entities
$user->setInfosAdmin();
$user->setInfosBank();
$user->setInfosStats();
$user->setApodis();
unset($userValues['userId']);
//this method inspect submitted field to check if they are mandatory or if current user is allowed to modify them
$this->fields = $this->getInfosFields();
foreach($userValues as $input=>$value){
list($entity,$input)=explode('-',$input);
if($input=='plainPassword' && $value==''){
continue;
}
if($entity=='Main'){
$setter = 'set'.ucfirst($input);
$user->$setter($value);
} else {
$setter = 'set'.ucfirst($input);
$join = 'get'.$entity;
$user->$join()->$setter($value);
}
}
return $user;
}
According to the comments your UsersMain property need to be like this:
/**
* Join administrative informations
* #ORM\OneToOne(targetEntity="MyVendor\User\UserBundle\Entity\UsersInfosAdmin", mappedBy="userMain", cascade={"persist"}, orphanRemoval=true)
*/
private $infosAdmin;
/**
* Setter InfosAdmin
*/
public function setInfosAdmin(UsersInfosAdmin $infosAdmin)
{
$this->infosAdmin = $infosAdmin;
$this->infosAdmin->setUserMain($this); <--- add this to set userMain on associated object
return $this;
}
then your UsersInfosAdmin properties need to be like this:
/**
* #var integer
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="MyVendor\User\UserBundle\Entity\UsersMain", inversedBy="userMain")
*/
private $userMain;

Symfony2: embedding form with many-to-many entities throws exception

Issue
I've been trying to follow the tutorial to embed a form in another one. What I'm trying to do here is add a task, and add multiple categories to it. I'm using the example at http://symfony.com/doc/current/book/forms.html#embedding-a-single-object, but I added some ORM annotations to make the relation many-to-many. As such, here is my code for the Task & Category entities:
Code
Task entity
<?php
namespace Acme\TaskBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Task
*
* #ORM\Table()
* #ORM\Entity
*/
class Task
{
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
* #ORM\JoinTable(name="tasks_categories")
*
* #Assert\Type(type="Acme\TaskBundle\Entity\Category")
*/
protected $categories;
// ...
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Task
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Constructor
*/
public function __construct()
{
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add categories
*
* #param \Acme\TaskBundle\Entity\Category $categories
* #return Task
*/
public function addCategorie(\Acme\TaskBundle\Entity\Category $categories)
{
$this->categories[] = $categories;
return $this;
}
/**
* Remove categories
*
* #param \Acme\TaskBundle\Entity\Category $categories
*/
public function removeCategorie(\Acme\TaskBundle\Entity\Category $categories)
{
$this->categories->removeElement($categories);
}
/**
* Get categories
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
}
Category entity
<?php
namespace Acme\TaskBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
/**
* Category
*
* #ORM\Table()
* #ORM\Entity
*/
class Category
{
/**
* #ORM\ManyToMany(targetEntity="Task", mappedBy="categories")
*/
private $tasks;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Category
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Constructor
*/
public function __construct()
{
$this->tasks = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add tasks
*
* #param \Acme\TaskBundle\Entity\Task $tasks
* #return Category
*/
public function addTask(\Acme\TaskBundle\Entity\Task $tasks)
{
$this->tasks[] = $tasks;
return $this;
}
/**
* Remove tasks
*
* #param \Acme\TaskBundle\Entity\Task $tasks
*/
public function removeTask(\Acme\TaskBundle\Entity\Task $tasks)
{
$this->tasks->removeElement($tasks);
}
/**
* Get tasks
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTasks()
{
return $this->tasks;
}
}
Both forms have been auto-generated by using the doctrine:generate:form command. I changed the TaskType form to include the categories:
TaskType form
<?php
namespace Acme\TaskBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('categories', new CategoryType())
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\TaskBundle\Entity\Task',
'cascade_validation' => true,
));
}
public function getName()
{
return 'task';
}
}
Now when I go to the create page for tasks, I get this error:
The form's view data is expected to be an instance of class
Acme\TaskBundle\Entity\Category, but is an instance of class
Doctrine\Common\Collections\ArrayCollection. You can avoid this error
by setting the "data_class" option to null or by adding a view
transformer that transforms an instance of class
Doctrine\Common\Collections\ArrayCollection to an instance of
Acme\TaskBundle\Entity\Category.
I honestly have no idea how to fix it since this seemed a pretty straight-forward thing but apparently it isn't. Could someone help me out here please?
in your Task Entity remove the validation for categories.
Symfony tries to validate a ArrayCollection as one Category!(hence the error)
* #Assert\Type(type="Acme\TaskBundle\Entity\Category")
*/
$categories;
It isn't necessary since it is a collection. (validation will be based on what type of objects are in the collection)
If you created a CategoryType form then this form should return Acme\TaskBundle\Entity\Category for it's data class.
class CategoryType {
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\TaskBundle\Entity\Category',
.....
));
}
}
Also, in your TaskType
$builder
->add('name')
->add('categories', new CategoryType()) // new CategoryType()
// is not really needed here,
// symfony will automatically detect
// it's relation and create a new
// CategoryType if necessary.

JMSSerializerBundleThe Response content must be a string or object implementing __toString()"

I've got a problem with JMSSerializerBundle.
I have my entity AGVote there :
<?php
namespace K\AGBundle\Entity;
use JMS\SerializerBundle\Annotation\Type;
use JMS\SerializerBundle\Annotation\Accessor;
use JMS\SerializerBundle\Annotation\AccessType;
use JMS\SerializerBundle\Annotation\Exclude;
use JMS\SerializerBundle\Annotation\ExclusionPolicy;
use Doctrine\ORM\Mapping as ORM;
/**
* K\AGBundle\Entity\AGVote
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*
*/
/*
*
/** #AccessType("public_method") */
class AGVote
{
/**
* #Type("integer")
* #Accessor(getter="getId")
*/
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="text")
* #Accessor(getter="getQuestion")
* #Type("text")
*/
public $question;
/**
* #ORM\Column(type="smallint")
* #Type("integer")
* #Accessor(getter="getActif")
*/
public $actif;
/**
* #ORM\ManyToOne(targetEntity="\K\KBundle\Entity\Users", cascade={"all"})
* #Exclude
*/
protected $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 K\KBundle\Entity\Province $Users
*/
public function setUsers(\K\KBundle\Entity\Users $users)
{
$this->users = $users;
}
/**
* Get Users
*
* #return K\KBundle\Entity\Users
*/
public function getUsers()
{
return $this->users;
}
public function __toString()
{
return $this->getquestion();
}
}
I have made a controller that juste return me an AGVote Entity in Json :
public function jsonvoteAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('KAGBundle:AGVote')->findOneById($id);
if ($entity->getActif() == 1) {
$serializer = $this->container->get('serializer');
$serializer->serialize($entity, 'json');
$response = new Response($serializer);
return $reponse;
}
}
I have a response in Json but it is a error saying :
[{"message":"The Response content must be a string or object implementing __toString(), \"object\" given.","class":"UnexpectedValueException","trace":
In fact I have already implement a __toString() method inside of all my entities.
Does anyone have an idea ?
Thanks you :)
When you call the serialize method on the $serializer, it returns the serialized data (a string).
The problem is that you do not use this returned value, and create the response with the $serializer itself, which makes no sense.
First, store the serialized $entity:
$serializedEntity = $serializer->serialize($entity, 'json');
Then, you can return a new response using with string:
return new Response($serializedEntity, 200, array('Content-Type' => 'application/json'));