symfony2 setter for related entity id - symfony

My relation in entity:
/**
* #ORM\ManyToOne(targetEntity="Group")
*/
protected $group;
now I have a setter method setGroup() to set related entity, but there seems no method setGroupId() to set group_id without the entity object. How can I set group_id directly?

I suggest you have a look at Doctrine EntityManager ->getReference() method.
$user->setGroup($em->getReference('Group', 10));

Use a custom repository to create a specific method that will fetch the group, and then set it with setGroup.
Edit: You can even directly add/update the id via a SQL query: https://stackoverflow.com/a/10215061
But that's dirty.

Try defining another field (groupId) and map it directly ot your field in the database.

Related

Set up non-persistent relation in Doctrine 2

I have an object $user that has a one to many relation with $establishment. I can use:
$user->getEstablishments();
The user can select a stablishment to work on. I have this method that I call in the controller:
$user->setCurrentEstablishment($establishment);
And this one that I call in the view:
$establishment = $user->getCurrentEstablishment();
I want to be able to call:
$user->setCurrentEstablishmentBy Slug($establishment_slug);
where the slug is a string, and let the user object look for the establishment.
Doctrine discourages the practice of accessing the Entity Manager inside the Entity object, but I think that using it in the controller is even worse.
I suspect that some special Doctrine annotation exists that takes care of non persistent relations like this, or some method other than serving the Entity Manager through a service should be used here. Some easy way of referencing other entities from inside the model.
¿Is there any? ¿How could I do that?
There is no Annotation in Doctrine which could convert slug into object.
What can help You is ParamConverter, with it you can automatically convert slug from query into object. But it still must be used in Controller.
Example usage:
/**
* #Route("/some-route/{slug}")
* #ParamConverter("object", class="AppBundle:Establishment", options={"id" = "slug", "repository_method" = "findEstablishmentBySlug"})
*/
public function slugAction(Establishment $object)
{
...
Docs about param converter: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html

Symfony - Get Entity from within another entity

I would like to retrieve a record from another entity (or record from the DB) within a entity.
They there are no relationship between the two entities.
I am using #ORM\HasLifecycleCallbacks() and #ORM\PrePersist so when the main entity is created it will also create another entity (save a record to another table)
The above is working fine, there are no issues with this.
What I am having an issue with is I would like to link that entity with another table but I need to retrieve the object based on the value of the first entity.
Usually I would write a function in the entity repository but I am not calling the entity manager within the entity.
An Entity in Doctrine is an object representation of a concept, with attributes and methods. It is meant to be lightweight, a POPO (plain old php object). It must not know anything about its persistence. Therefore if you see reference to the EntityManager in a model, it probably stinks.
Solutions? You could use an entity listener called on entity creation and then use a service dedicated only to properly compose your object(s), maybe something like a Factory. In this way, your entity stays lightweight, the lifecycle management is satisfied and the entity composing is responsibility only of your service.
Entity manager is accessible in an entity repository. You can legally use it to fetch data from other entities and to compose your business logic. This is what entity repositories are made for: Doctrine Custom Repositories, Symfony Custom Repository Classes.
/**
* #ORM\Entity
*/
class Beta {}
/**
* #ORM\Entity
*/
class Alpha {}
class AlphaRepository extends EntityRepository
{
public function getDataFromAnotherEntity($something)
{
$query = 'select * from MyBundle\Entity\Alpha alpha where alpha.id = :something';
return $this->getEntityManager()
->createQuery($query)
->setParameter('something', $something)
->getResult();
}
}
In Symfony 3.1 you can use the entityManager to set a reference. This is still lightweight as it does not instance a complete Doctrine Record.
Example: I have an entity Status which has some states, and it's referenced in another entity. On create i use this method inside EventSubscriber:
public function preAction(LifecycleEventArgs $args)
{
$entity = $args->getObject();
$entityManager = $args->getObjectManager();
if (method_exists($entity, 'setStatus')) {
if ($entity->getStatus() === null) {
$entity->setStatus($entityManager->getReference('AppBundle\Entity\Status', Status::STATUS_REGULAR));
}
}
}

One-to-many relationship: how to set default entity-value?

I have a one-to-many relationship Cart-SendingMethod. I would like to set a default SendingMethod for new Carts. So I have tried this:
<?php
/**
* #ORM\ManyToOne(targetEntity="MetodoEnvio", inversedBy="metodoEnvios")
* #ORM\JoinColumn(name="metodo_envio_id", referencedColumnName="id")
**/
private $metodoEnvio = 1;
but doesn't work... I get:
Impossible to access an attribute ("id") on a integer variable ("1") when I call Cart.SendingMethod.id from a view file
So how to set a default SendingMethod for new Products?
I could do it in the controller, but I would like to know if it is possible from the entity Product.
Note: I didn't know exactly if this is a symfony or doctrine question.
You don't want to introduce dependencies into your entity.
The obvious and cleaner way to do it would be to create a CartFactory service, and inject that into any controller (or other class) that needs to create carts. Inject your EntityManager and other dependencies into the factory. That way you DRY up your cart-initialization code, and avoid bulking up your controller.
Just set the property's default value inside the constructor like this:
public function __construct(..)
{
$this->property = new OtherObject();
}

How to handle SQL triggers in Symfony2?

I have a User entity in my Symfony2/Doctrine2 webapp. This user has an attribute last_updated to identify the latest time, anything has changed. I set this attribute to NOT NULL in my database. So far, so good.
I would consider it to be good practice to create a SQL trigger in the database, that sets this last_updated to NOW() on every INSERT or UPDATE. So you don't have to care about this in your application. So that's what I did, I implemented this trigger in my database.
But if I now create a user in my app
$user = new User();
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
I get an error message by Symfony:
An exception occurred while executing 'INSERT INTO User (username, ..., last_updated) VALUES (?, ..., ?)'
with params ["johndoe", ..., null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'last_updated' cannot be null
The problem is clear: Symfony is trying to fire an INSERT-statement to the database with the parameter null for last_updated, which is not allowed - as this attribute may not be null.
I could quickly think of two workarounds:
One workaround would be to take the last_updated field out of the entity description. Then Symfony would not try to pass anything to the database for this column, and the trigger would set the appropriate value. But I don't think this is a good way, because as soon as I would try to update the db schema (doctrine:schema:update --force) I would loose my last_updated-column.
Another workaround: Simply do $user->setLastUpdated(new \DateTime()) before I persist() and flush(). But this would minimize the advantage of using a trigger on my database to avoid having to care about it in my application.
Is there any way to let Symfony/Doctrine know that there is a trigger running on my database? If not, (how) can I hook into Symfony/Doctrine to implement a proper workaround?
To quote a response to this question from a google group:
Database side code (such as Triggers and Functions) tend to break the benefits of developing software using an ORM like Propel or Doctrine as one of the biggest advantages of using ORM's is to be database agnostic. By having database side Triggers and Functions you are tying yourself to the database and therefore gain little to no benefit using an ORM. -GarethMc
https://groups.google.com/forum/#!topic/symfony-users/MH_ML9Dy0Rw
For this it is best to use the Life Cycle Callbacks as Faery suggests. One simple function will handle updating that field so that you dont have to worry about it if you decide to change databases in the future.
//In Your Entity File EX: SomeClass.php
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class SomeClass
{
....
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function prePersistPreUpdate()
{
$this->last_modified = new \DateTime();
}
}
See also references for lifecycle callbacks
Symfony reference
Doctrine reference
In your case you would add the lifecycle call back function and annotation to your User entity class. SomeClass is simply an example class showing that lifecycle callbacks are good for more than just your User entity.
Another (easier and more generalized) option would be to use the Timestampable Doctrine extension by Gedmo. In this way, you could simply annotate your entity fields to be timestamped on create or on update.
Example:
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
class MyEntity
{
...
/**
* #var \DateTime $lastUpdated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(name="last_updated", type="datetime")
*/
private $lastUpdated;
...
}
https://packagist.org/packages/gedmo/doctrine-extensions

Symfony 2 - flush in postUpdate fire preUpdate event

I detected this problem "thanks" to an exception I got:
Catchable Fatal Error: Argument 3 passed to
Doctrine\ORM\Event\PreUpdateEventArgs::__construct()
must be an array, null given, called in
/.../vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php on line 804
and defined in
/.../vendor/doctrine/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php line 28
I am working on a project that requieres a specific logic:
When the order field in entity book is modified, I need to update field books_order_modified_at in the parent entity bookstore (this field allows me to know whether the order of books in a bookstore was changed).
I decided to do it in an event listener since there are many places in the code that might change the order of books.
I didn't find any way to update a related entity from preUpdate event, so I have a private field in the listener class which I use to tell the postUpdate event to update the relevant bookstore entity.
My problem is that when I do so the preUpdate event of the book entity is fired.
When I check the change-set it contains only the modified_at field, but it has the same value before and after.
If someone has another idea how to solve the problem - great.
If not - any idea how can I prevent the preUpdate event from being fired when the flush is called in teh postUpdate event??
Actually, this is a problem from doctrine Doctrine Issue DDC-2726. Solved it by adding a clear call on the entity manager after the flush in the listener so the 3-rd argument to that constructor, which is actually the entityChangeSets, will be re-written.
What about updating the modified_at within your entities and let doctrine handle it? You would change your setOrder method in your book to update the BookOrder entity like this:
class Book {
public function setOrder($order) {
// modify book
$this->bookOrder->updateModifiedAt();
}
}
Of course your BookOrder would have to implement modifiedAt:
class BookOrder {
public function updateModifiedAt() {
$this->modifiedAt = new \DateTime();
}
}
If you use other classes for your Datetime, you of course have to change this code!
Doctrine should recognize that BookOrder has changed and should update it without any need to use a event listener.
I can suggest you to use Timestampable extension for Doctrine from DoctrineExtensionsBundle.
By using it you don't need to set created_at or modified_at values. This extension does it automatically. Even it can set modified_at only when specific fields were modified. See example.
I think you are writing something like this extension. So, you don't need to do that because this is already done :)
I had a similar problem to this. Trying to use preupdate to modify child elements caused the same error. In the end, my solution to simply update the children belonging to the parent. No explicit call to flush required.
/**
* Update expiry dates for all runners belonging to a campaign
*
* #param $runners
* #param $expiryDate
*/
private function updateCampaignRunners($runners, $expiryDate){
foreach($runners as $runner){
$runner->setExpiresAt($expiryDate);
$this->getModelManager()->update($runner);
}
}
/**
* Post update and persist lifecycle callback
*
* #param Campaign $campaign
*/
private function postAction(Campaign $campaign)
{
$runnerExpire = $this->getForm()->get("runnerExpire")->getData();
if($runnerExpiryDate && $campaign->getRunners()){
$this->updateCampaignRunners($campaign->getRunners(), $runnersExpiryDate);
}
}

Resources