Let's say that I have an entity.
This entity has two field that should be changed only through one function, because of domain logic
How could I make the form framework to set the values using one method call.
What I've read about data transformers, lead me to the impression that it can not be used to this propose.
Next is form events, this are the available events
PRE_BIND
BIND
POST_BIND
PRE_SET_DATA
POST_SET_DATA
BIND_CLIENT_DATA
BIND_NORM_DATA
SET_DATA
but the documentation about this is very scarce.
This is a sample entity
<?php
namespace X3\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Test
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="X3\TestBundle\Entity\TestRepository")
*/
class Test
{
/**
* #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;
/**
* #var string
*
* #ORM\Column(name="firstValue", type="string", length=255)
*/
private $firstValue;
/**
* #var string
*
* #ORM\Column(name="secondValue", type="string", length=255)
*/
private $secondValue;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Test
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set firstValue
*
* #param string $firstValue
* #return Test
*/
protected function setFirstValue($firstValue)
{
$this->firstValue = $firstValue;
return $this;
}
/**
* Get firstValue
*
* #return string
*/
public function getFirstValue()
{
return $this->firstValue;
}
/**
* Set secondValue
*
* #param string $secondValue
* #return Test
*/
protected function setSecondValue($secondValue)
{
$this->secondValue = $secondValue;
return $this;
}
/**
* Get secondValue
*
* #return string
*/
public function getSecondValue()
{
return $this->secondValue;
}
//
// The objective here is that the form use this function
// to set the two values in one call
//
protected function setValues($firstValue, $secondValue)
{
$this->firstValue = $firstValue;
$this->secondValue = $secondValue;
return $this;
}
}
Do note that setFirstValue($firstValue) and setSecondValue($secondValue) are protected, the values should be set using the method setValues($firstValue, $secondValue)
Is there an event I can use, to retrieve the firstValue and secondValue and set it using setValues($firstValue, $secondValue) and avoid the form component to complain about Method "setFirstValue()" is not public in class...?
Some code or link to it, would be a bonus.
Extending the form framework would not be easy, even if using some of form events.
One possible solution would be simply create values property in the entity which isn't mapped to any database field, and its setter and getter. Then the setValues function can do whatever needed to set firstValue and secondValue.
You may want to use DataTransformer. It allows you to control the transformation of data in both directions. Anyway you need to add values field to form, that will transformed to two fields and saved as one field.
Related
I want to set id manual I write this code in my Test entity:
can I use setId() for entities like my code?
My code is here:
/**
* Test
* #ORM\Table(name="test")
*/
class Test
{
/**
* #var int
* #ORM\Column(name="id", type="integer")
*/
private $id;
/**
* #var string
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Set id
* #param integer $id
* #return Test
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Get id
* #return integer
*/
public function getId()
{
return $this->id;
}
// other methods
}
is this correct way to set id?
if not what is the correct and standard way?
You can use your own primary key, telling to Doctrine not to generate value ...
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/annotations-reference.html#annref_generatedvalue
/**
* Test
* #ORM\Table(name="test")
*/
class Test
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
* #ORM\Column(name="id", type="integer")
*/
private $id;
/**
* Set id
* #param integer $id
* #return Test
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
Don't forget setId before persist!
Doctrine expects the primary key of your entity to be immutable (non-changeable) after the entity is persisted/flushed to the database (or fetched from DB).
The code you wrote is perfectly correct in terms of PHP but will most likely break doctrine functionality if you ever use setId().
If you are interested in the internals, look up "Doctrine identity maps"
why Symfony2 performs 40 DB queries if I use following code:
$em = $this->getDoctrine()->getManager();
$records = $em->getRepository('MyWebBundle:Highlight')->findAll();
I thought that findAll() method returns only all items from Highlight entity and associations to other entities replaces Proxy objects. But now findAll() method gettings all associations entities.
Do you know where is the problem ?
indexAction
public function indexAction() {
$em = $this->getDoctrine()->getManager();
$records = $em->getRepository('MyWebBundle:Highlight')->findAll();
$csrf = $this->get('security.csrf.token_manager');
$token = $csrf->refreshToken(self::FORM_TOKEN_ID);
$params = array(
"data" => array(
"all" => $records,
),
"token" => $token->getValue(),
"static" => array(
"add" => $this->generateUrl("admin_highlight_add"),
"edit" => $this->generateUrl("admin_highlight_edit"),
"del" => $this->generateUrl("admin_highlight_del"),
),
);
$ser = $this->get('jms_serializer');
$jsonContent = $ser->serialize($params, 'json');
return array('jsonContent' => $jsonContent);
}
Highlight entity
namespace My\WebBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
/**
* Highlight
*
* #JMS\ExclusionPolicy("none")
* #ORM\Table()
* #ORM\Entity(repositoryClass="My\WebBundle\Entity\HighlightRepository")
*/
class Highlight {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="abbreviation", type="string", length=8, unique=true)
*/
private $abbreviation;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=80, nullable=true)
*/
private $description;
/**
* #var string
*
* #ORM\Column(name="color", type="string", length=7)
*/
private $color;
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="Goods", mappedBy="highlight")
*/
private $goods;
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="Calibration", mappedBy="highlight")
*/
private $calibrations;
/**
* Constructor
*/
public function __construct() {
$this->goods = new \Doctrine\Common\Collections\ArrayCollection();
$this->calibrations = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set abbreviation
*
* #param string $abbreviation
* #return Highlight
*/
public function setAbbreviation($abbreviation) {
$this->abbreviation = $abbreviation;
return $this;
}
/**
* Get abbreviation
*
* #return string
*/
public function getAbbreviation() {
return $this->abbreviation;
}
/**
* Set description
*
* #param string $description
* #return Highlight
*/
public function setDescription($description) {
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription() {
return $this->description;
}
/**
* Set color
*
* #param string $color
* #return Highlight
*/
public function setColor($color) {
$this->color = $color;
return $this;
}
/**
* Get color
*
* #return string
*/
public function getColor() {
return $this->color;
}
/**
* Add goods
*
* #param \My\WebBundle\Entity\Goods $goods
* #return Highlight
*/
public function addGood(\My\WebBundle\Entity\Goods $goods) {
$this->goods[] = $goods;
return $this;
}
/**
* Remove goods
*
* #param \My\WebBundle\Entity\Goods $goods
*/
public function removeGood(\My\WebBundle\Entity\Goods $goods) {
$this->goods->removeElement($goods);
}
/**
* Get goods
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getGoods() {
return $this->goods;
}
/**
* Add calibrations
*
* #param \My\WebBundle\Entity\Calibration $calibrations
* #return Highlight
*/
public function addCalibration(\My\WebBundle\Entity\Calibration $calibrations) {
$this->calibrations[] = $calibrations;
return $this;
}
/**
* Remove calibrations
*
* #param \My\WebBundle\Entity\Calibration $calibrations
*/
public function removeCalibration(\My\WebBundle\Entity\Calibration $calibrations) {
$this->calibrations->removeElement($calibrations);
}
/**
* Get calibrations
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCalibrations() {
return $this->calibrations;
}
}
Highlight repository is empty
I think the problem comes from the serializer. Since you serializer highliths, each of them has their properties serialized as well which means that lazy query will be performed to retieved Goods which will be also serialized.
You should then prevent this behaviour by adding annotations to highlight's goods property as this
use ...
use JMS\SerializerBundle\Annotation\ExclusionPolicy;
use JMS\SerializerBundle\Annotation\Exclude;
/**
* ...
* #ExclusionPolicy("none")
*/
class Highlight
{
/**
* ...
* #Exclude
*/
private $goods;
}
You can have further details about exclusion stratigies from JMSSerializer doc
findAll itself does not perform many queries. Queries are executed when you access related entity via getters. As relation are not fetched eagerly, they first time are fetched when you are acessing them.
I think serializer access all children to send your object.
See Doctrine documentation
Whenever you have a managed entity instance at hand, you can traverse
and use any associations of that entity that are configured LAZY as if
they were in-memory already. Doctrine will automatically load the
associated objects on demand through the concept of lazy-loading.
To prevent this either disable children serialization or use fetch EAGER or build a DQL query, which prefetch all the children alongside with parents, like (just sample, not valid DQL)
SELECT Highlight, Good, Calibration
FROM Highlights Highlight
LEFT JOIN Highlight.googs Good
LEFT JOIN Goog.calibrations Calibration
WHERE ...
I'm attempting to accomplish BASIC inheritance in Doctrine 2, but I'm running into several major issues. Such a task should not be so complicated. Let's get down to business...
I have three classes, BaseFoodType, Drink, and Snack. My BaseFoodType has the following class definition:
/** #ORM\MappedSuperclass */
class BaseFoodType {
/**
* #ORM\Column(type="integer", length=7)
*/
public $budget = 0;
}
Which follows the instructions for inheritance on the doctrine website: http://docs.doctrine-project.org/en/2.0.x/reference/inheritance-mapping.html
Here is what the sub-classes look like prior to generating my entities:
namespace MySite\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* MySite\MainBundle\Entity\EventDrink
*
* #ORM\Table(name="drink")
* #ORM\Entity
*/
class Drink extends BaseFoodType {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="integer", length=5, nullable=true)
*/
public $people_count;
}
Both Drink, and Snack inherit from this base class but I'm running into numerous issues when attempting to build my entities using the doctrine:generate:entities command. First, Symfony inserts a private "budget" property into each subclass, along with getters and setters (THIS DEFEATS THE PURPOSE INHERITANCE)
/**
* #var integer
*/
private $budget;
/**
* Set budget
*
* #param integer $budget
*/
public function setBudget($budget)
{
$this->budget = $budget;
return $this;
}
/**
* Get budget
*
* #return integer
*/
public function getBudget()
{
return $this->budget;
}
Second, I'm getting a fatal error:
Fatal error: Access level to MySite\MainBundle\Entity\Drink::$budget
must be public (as in class MySite\MainBundle\Entity\BaseFoodType) in
C:\xampp\htdocs\MySite\src\MySite\MainBundle\Entity\Drink.php on line
197
I could probably make the generated properties public and be on my way, but again, that defeats the purpose of inheritance!
Thanks in advance for any insight.
Doctrine provides the means to specify the visibility of generated fields. Either protected or private. The default is private.
The problem is that the Symfony command that invokes Doctrine offers no way to change this.
Creating your own subclass of the standard Symfony command will allow you more control over the generation process. This might help you along.
namespace Foo\Bundle\FooBundle\Command;
use Doctrine\Bundle\DoctrineBundle\Command as DC;
use Doctrine\ORM\Tools\EntityGenerator;
class GenerateEntitiesDoctrineCommand extends DC\GenerateEntitiesDoctrineCommand
{
protected function configure()
{
parent::configure();
$this->setName('foo:generate:entities');
}
/**
* get a doctrine entity generator
*
* #return EntityGenerator
*/
protected function getEntityGenerator()
{
$entityGenerator = new EntityGenerator();
$entityGenerator->setGenerateAnnotations(true);
$entityGenerator->setGenerateStubMethods(true);
$entityGenerator->setRegenerateEntityIfExists(false);
$entityGenerator->setUpdateEntityIfExists(true);
$entityGenerator->setNumSpaces(4);
$entityGenerator->setAnnotationPrefix('ORM\\');
$entityGenerator->setFieldVisibility($entityGenerator::FIELD_VISIBLE_PROTECTED);
return $entityGenerator;
}
}
This does two things. It sets the property visibility to protected. This prevents php errors.
$entityGenerator->setFieldVisibility($entityGenerator::FIELD_VISIBLE_PROTECTED);
It also copies the annotations from mapped super class into the entity class.
$entityGenerator->setGenerateAnnotations(true);
Here's some example code where properties are inherited from a base class and their visibility and annotations copy correctly into the inheriting class
/** #ORM\MappedSuperclass */
class DataSuper {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Campaign", inversedBy="data")
* #ORM\JoinColumn(name="campaign_id", referencedColumnName="id")
* #Exclude
*/
protected $campaign;
/**
* #ORM\Column(type="text", nullable=true, name="data")
*/
protected $data;
/**
* #ORM\Column(type="datetime")
*/
protected $createdDate;
}
/**
* #ORM\Entity(repositoryClass="Foo\Bundle\FooBundle\Entity\DataRepository")
* #ORM\Table(name="data")
* #ExclusionPolicy("none")
*/
class Data extends DataSuper
{
}
After generation the Data class looks like:
class Data extends DataSuper
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="data", type="text", precision=0, scale=0, nullable=true, unique=false)
*/
protected $data;
/**
* #var \DateTime
*
* #ORM\Column(name="createdDate", type="datetime", precision=0, scale=0, nullable=false, unique=false)
*/
protected $createdDate;
/**
* #var \Foo\Bundle\FooBundle\Entity\Campaign
*
* #ORM\ManyToOne(targetEntity="Foo\Bundle\FooBundle\Entity\Campaign", inversedBy="data")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="campaign_id", referencedColumnName="id", nullable=true)
* })
*/
protected $campaign;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set data
*
* #param string $data
* #return Data
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* Get data
*
* #return string
*/
public function getData()
{
return $this->data;
}
/**
* Set createdDate
*
* #param \DateTime $createdDate
* #return Data
*/
public function setCreatedDate($createdDate)
{
$this->createdDate = $createdDate;
return $this;
}
/**
* Get createdDate
*
* #return \DateTime
*/
public function getCreatedDate()
{
return $this->createdDate;
}
/**
* Set campaign
*
* #param \Foo\Bundle\FooBundle\Entity\Campaign $campaign
* #return Data
*/
public function setCampaign(\Foo\Bundle\FooBundle\Entity\Campaign $campaign = null)
{
$this->campaign = $campaign;
return $this;
}
/**
* Get campaign
*
* #return \Foo\Bundle\FooBundle\Entity\Campaign
*/
public function getCampaign()
{
return $this->campaign;
}
}
And the table structure is correct once you do:
php app/console doctrine:schema:update --force
The exception is being thrown because BaseFoodType::budget is a public property and doctrine:generate:entities created a private property in your Drink / Snack classes extending BaseFoodType ( which is not correct but the way the command works by now ).
Property visibility in a subclass can only be the same level or more liberate ( private -> protected -> public ) but never more restrictive.
doctrine:generate:entities did not take superclass's public property into account when generating the getters/setters as the implementation with a public property is non-standard.
Therefore you will have to adjust the generated class manually.
I recommend using private/protected properties combined with getters & setters.
I have followed One-to-Many relation not working and created a one to many relationship
i have a users table where i have following fields
- id(primary key)
- name
- pwd
i have attachments table where user can upload more than one file i.e one user_id contains multiple files
- id
- user_id(foreignkey)
- path
my user entity contains the following code
namespace Repair\StoreBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* users
* #ORM\Entity(repositoryClass="Repair\StoreBundle\Entity\usersRepository")
*/
class users
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
--some code --
/**
* #ORM\OneToMany(targetEntity="attachments", mappedBy="user_id")
*/
private $attachments;
public function __construct()
{
$this->attachments= new ArrayCollection();
}
/**
* Add attachments
*
* #param \Repair\StoreBundle\Entity\attachments $attachments
* #return users
*/
public function addAttachment(\Repair\StoreBundle\Entity\attachments $attachments)
{
$this->attachments[] = $attachments;
return $this;
}
/**
* Remove attachments
*
* #param \Repair\StoreBundle\Entity\attachments $attachments
*/
public function removeAttachment(\Repair\StoreBundle\Entity\attachments $attachments)
{
$this->attachments->removeElement($attachments);
}
/**
* Get attachments
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttachments()
{
return $this->attachments;
}
this is my attachments entity
namespace Repair\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* attachments
*/
class attachments
{
-- some code for id--
private $id;
/**
* #var integer
* #ORM\Column(name="user_id", type="integer", nullable=false)
* #ORM\ManyToOne(targetEntity="users", inversedBy="users")
* #ORM\JoinColumn(name="user_id", referencedColumnName="user_id")
*/
protected $userId;
public function getId()
{
return $this->id;
}
/**
* Set userId
* #param integer $userId
* #return attachments
*/
public function setUserId($userId)
{
$this->userId = $userId;
return $this;
}
/**
* Get userId
*
* #return integer
*/
public function getuserId()
{
return $this->userId;
}
--Some code for paths --
}
It is not displaying any errors
but how to know whether the foriegn key is set or not i went to phpmyadmin and checked the indexes it only shows the primary keys.please say whether i did correct or not and how to check whether foreign key is set or not
problem is in your annotations. In your attachment entity you should have annotation like this.
* #ORM\ManyToOne(targetEntity="users", inversedBy="annotations")
you don't have to have join column annotation. But if you want to have it there, it should look like this.
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
Also it shouldn't be called user_id but user, because doctrine takes it as whole entity.
I would like to save my users search on my website. Thaht's why i have a Class User and i would like to create Search Class.
I have done that :
class Search
{
public function __construct()
{
$this->searched_date = new \Datetime();
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="test\UserBundle\Entity\User")
*/
private $user;
/**
* #ORM\Column(name="termsearch", type="string", length=255, nullable="true")
*/
private $termsearch;
/**
* #ORM\Column(name="goodtitle", type="string", length=255, nullable="true")
*/
private $goodtitle;
/**
* #ORM\Column(name="searched_date", type="datetime")
*/
private $searched_date;
/**
* Set termsearch
*
* #param text $termsearch
*/
public function setTermsearch($termsearch)
{
$this->termsearch = $termsearch;
}
/**
* Get termsearch
*
* #return text
*/
public function getTermsearch()
{
return $this->termsearch;
}
/**
* Set searched_date
*
* #param datetime $searchedDate
*/
public function setSearchedDate($searchedDate)
{
$this->searched_date = $searchedDate;
}
/**
* Get searched_date
*
* #return datetime
*/
public function getSearchedDate()
{
return $this->searched_date;
}
/**
* Set user
*
* #param test\UserBundle\Entity\User $user
*/
public function setUser(\test\UserBundle\Entity\User $user)
{
$this->user = $user;
}
/**
* Get user
*
* #return test\UserBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Set goodtitle
*
* #param text $goodtitle
*/
public function setGoodtitle($goodtitle)
{
$this->goodtitle = $goodtitle;
}
/**
* Get goodtitle
*
* #return text
*/
public function getGoodtitle()
{
return $this->goodtitle;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
And i would like to insert like that :
$em = $this->getDoctrine()->getEntityManager();
$user = $em->getRepository('TestUserBundle:User')->find($currentuser->getID());
$search = new Search();
$search->setUser($user);
$search->setTermsearch($termsearch);
$search->setGoodtitle($goodtitle);
$em->persist($search);
$em->flush();
Unfortunately i have this error :
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens (500 Internal Server Error)
And in the stack we can found that :
INSERT INTO s_search (user_id, termsearch, goodtitle, searched_date) VALUES (?, ?, ?, ?) ({"1":"c2c","2":"C2C Down The Road","3":{"date":"2012-10-31 00:18:47","timezone_type":3,"timezone":"Europe\/Paris"}})
I don't know how i can create this class Search...
Thank for for you help !
Remove #ORM\Id from the $user as #ManyToOne mapping reference does not require a type. See Doctrine's Annotation Reference for details. Doctrine takes care of the correct column type to hold the reference to another entity.
Make also sure that your User query really returns a valid $user. If it's possible that Search does not have $user, use #JoinColumn annotation to claim the column nullable. See another SO question Doctrine 2 can't use nullable=false in manyToOne relation?