Symfony2 Delete Record - symfony

I am having issues removing a record from my db using Symfony2. Hoping someone can help me out.
Here is my code:
// Get user's account
$account = $this->getUser()->getAccount();
// Get manager
$em = $this->getDoctrine()->getManager();
// Get entity
$entity = $em->getRepository('WICPurchaseOrderLineItemBundle:PurchaseOrderLineItem')->findBy(array('account'=>$account->getId(), 'id'=>$id));
// If not entity
if (!$entity) {
throw $this->createNotFoundException('Unable to find this entity.');
}
// Remove the record...
$em->remove($entity);
$em->flush();
// Go to this url...
return $this->redirect($this->generateUrl('purchaseOrder_view', array('id' => '8')));
When this is run, I get this error:
EntityManager#remove() expects parameter 1 to be an entity object, array given.
My URL look like this:
{{ path('purchase_order_remove_line_item', { 'id': purchaseOrderLineItem.id }) }}
Does my "id" number need to be turned into an object first? Not sure how to fix this, still learning Symfony.
Anyone have any suggestions?

You just need to use the findOneBy method instead of the findBy method.
$entity = $em->getRepository('WICPurchaseOrderLineItemBundle:PurchaseOrderLineItem')->findOneBy(array('account'=>$account->getId(), 'id'=>$id));

Related

I get error on Adding custom action in symfony Warning: array_merge(): Expected parameter 2 to be an array, null given

I have a dating website programmed with symfony. I want to add a function in easyadmin to be able to delete a user and her/his messages and posts. In UserCrudController I followed the instruction in symfony website and write a custom action. But I get an error
Warning: array_merge(): Expected parameter 2 to be an array, null given
public function configureActions(Actions $actions): Actions
{
// this action executes the 'renderInvoice()' method of the current CRUD controller
$delUserAction = Action::new('deleteUser', 'Delete user completely')
->linkToRoute('delete_user', function (User $user){
$id = $user->getId();
$res = $this->getDoctrine()->getRepository(User::class)->find($id);
if ($res) {
$em = $this->getDoctrine()->getManager();
$em->remove($res);
$em->flush();
}
$res = $this->getDoctrine()->getRepository(Message::class)->remove_all_message($id);
$res = $this->getDoctrine()->getRepository(Beziehungen::class)->remove_all_relations($id);
$res = $this->getDoctrine()->getRepository(Album::class)->remove_album_of_user($id);
$res = $this->getDoctrine()->getRepository(Blog::class)->remove_blog_of_user($id);
$res = $this->getDoctrine()->getRepository(Comment::class)->remove_comments_of_user($id);
$res = $this->getDoctrine()->getRepository(Subcomment::class)->remove_subcomments_of_user($id);
$res = $this->getDoctrine()->getRepository(Like::class)->remove_likes_of_user($id);
});
return $actions
// ...
->add(Crud::PAGE_INDEX, $delUserAction)
;
}
Can you please help me?
According to the tutorial, linkToRoute will connect an action to a route (as the name suggests). Similar to other route functions, it expects as first parameter the route name and as second parameter an array or a function that returns an array - the parameters to fill the placeholders of the route.
Your function doesn't return an array but instead IS the action.
So you really should put the deletion code you have and put it into an extra function like ... deleteUserAction, and then define your action as
$delUserAction = Action::new('deleteUser', 'Delete user completely')
->linkToCrudAction('deleteUserAction');

Doctrine weird behavior, changes entity that I never persisted

I have this situation:
Symfony 4.4.8, in the controller, for some users, I change some properties of an entity before displaying it:
public function viewAction(string $id)
{
$em = $this->getDoctrine()->getManager();
/** #var $offer Offer */
$offer = $em->getRepository(Offer::class)->find($id);
// For this user the payout is different, set the new payout
// (For displaying purposes only, not intended to be stored in the db)
$offer->setPayout($newPayout);
return $this->render('offers/view.html.twig', ['offer' => $offer]);
}
Then, I have a onKernelTerminate listener that updates the user language if they changed it:
public function onKernelTerminate(TerminateEvent $event)
{
$request = $event->getRequest();
if ($request->isXmlHttpRequest()) {
// Don't do this for ajax requests
return;
}
if (is_object($this->user)) {
// Check if language has changed. If so, persist the change for the next login
if ($this->user->getLang() && ($this->user->getLang() != $request->getLocale())) {
$this->user->setLang($request->getLocale());
$this->em->persist($this->user);
$this->em->flush();
}
}
}
public static function getSubscribedEvents()
{
return [
KernelEvents::TERMINATE => [['onKernelTerminate', 15]],
];
}
Now, there is something very weird happening here, if the user changes language, the offer is flushed to the db with the new payout, even if I never persisted it!
Any idea how to fix or debug this?
PS: this is happening even if I remove $this->em->persist($this->user);, I was thinking maybe it's because of some relationship between the user and the offer... but it's not the case.
I'm sure the offer is persisted because I've added a dd('beforeUpdate'); in the Offer::beforeUpdate() method and it gets printed at the bottom of the page.
alright, so by design, when you call flush on the entity manager, doctrine will commit all the changes done to managed entities to the database.
Changing values "just for display" on an entity that represents a record in database ("managed entity") is really really bad design in that case. It begs the question what the value on your entity actually means, too.
Depending on your use case, I see a few options:
create a display object/array/"dto" just for your rendering:
$display = [
'payout' => $offer->getPayout(),
// ...
];
$display['payout'] = $newPayout;
return $this->render('offers/view.html.twig', ['offer' => $display]);
or create a new non-persisted entity
use override-style rendering logic
return $this->render('offers/view.html.twig', [
'offer' => $offer,
'override' => ['payout' => $newPayout],
]);
in your template, select the override when it exists
{{ override.payout ?? offer.payout }}
add a virtual field (meaning it's not stored in a column!) to your entity, maybe call it "displayPayout" and use the content of that if it exists

How to implement a Logical Erase in Symfony 3?

I am implementing a logical erase for my Symfony 3 entities.
On my entities, I added a $deleted field, and I created some delete-controllers with this code:
$entity->setDeleted(true);
$em->persist($entity);
$em->flush();
Then, I modified also my queries to avoid select 'deleted' entities. And works great.
The problem:
I have some entities with Unique Constraint (for example, email field on user table), so when I delete an user, and then try to add the same user with the same email, symfony shows a validation form error due to 'duplicated email'.
I tried to control this on controller in the following way:
$user = new User();
$em = $this->getDoctrine()->getManager();
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
//Check for deleted duplication:
$duplicatedUser = $em->getRepository('AppBundle:User')
->getDuplicatedAndDeletedUser($user);
if($duplicatedUser != null){
$em->remove($duplicatedUser);
$em->flush();
}
if ($form->isValid()) {
$em->persist($user);
$em->flush();
}
}
But this code doesn't avoid the form validation error. First time when I try to create, Stymfony shows an error, and then, if I resubmit the form, it works because of the duplicated entity was removed from db.
How can I solve this issue?
Note: I know this: http://atlantic18.github.io/DoctrineExtensions/doc/softdeleteable.html but, I have already developed all the logic described, so I prefer go in my way with this.
Finally I find the sollution.
As you can see here http://symfony.com/doc/current/forms.html#handling-form-submissions handleRequest() method validate the form, so delete the entity after that don't solve the problem at all.
I do that to get form data:
$userName = $request->request->get('user')['email'];
And then check if this $userName is already used. If so, I delete the user before handleRequest() call.
Hope this help others.
Something strange in your code, I can see a user getting created but it doesn't get injected in the form so it can be used to store data in it and I also don't see the form anywhere, was it created in the controller or is it pure html, if so, you would need to fill in the user with the data from the form with $formName->getData() function or if you so wish, fill in only individual fields with the $formName['fieldname']->getData(). Try this code:
$user = new User();
$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
$em = $this->getDoctrine()->getManager();
//Check for deleted duplication:
$duplicatedUser = $em->getRepository('AppBundle:User')
->getDuplicatedAndDeletedUser($user);
if($duplicatedUser != null){
$em->remove($duplicatedUser);
$em->flush();
}
$em->persist($user);
$em->flush();
}

Doctrine EventListener onFlush does not save original entity

I've been struggling with an annoying issue for a while. I'm trying to create Notification entities associated to InventoryItems that expire.
These InventoryItems are generated automatically for users, but users can edit them and set expiry date on them individually. Upon saving, if an InventoryItem has an expiry date, a Notification entity is generated and associated to it. So these Notification entities are created when an entity is updated and hence onPersist event is not going to work.
Everything seems to work fine and Notifications are generated upon saving InventoryItems as expected. Only problem is, when a Notification is created for the first time, even though it's saved properly, changes to the InventoryItem are not saved. That is a Notification with correct expiry date is created, but the expiry date is not saved on the InventoryItem.
Here's my onFlush code:
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof NotificableInterface) {
if ($entity->generatesNotification()){
$notification = $this->notificationManager->generateNotificationForEntity($entity) ;
if ( $notification ) {
$uow->persist($notification) ;
}
$entity->setNotification($notification) ;
$uow->persist($entity);
$uow->computeChangeSets();
}
}
}
}
The problem only occurs the first time a Notification is associated to an entity, i.e. the first time an expiry date is set on an InventoryItem. In later instances when expiry date is updated, the update is reflected correctly on both the Notification and InventoryItem.
Any ideas or suggestions would be appreciated.
Thanks
You need to call computeChangeset specifically on your newly created or updated entity. Just calling computeChangeSets is not enough.
$metaData = $em->getClassMetadata('Your\NameSpace\Entity\YourNotificationEntity');
$uow->computeChangeSet($metaData, $notification);
Thanks Richard. You pointed me to the right direction. I needed to recompute the change set on the parent entity (InventoryItem) to get things working properly. Additionally I had to call computeChangeSets on the unit of work to get rid of the invalid parameter number error (such as the one explained symfony2 + doctrine: modify a child entity on `onFlush`: "Invalid parameter number: number of bound variables does not match number of tokens")
Note I ended up also removing:
if ( $notification ) {
$uow->persist($notification) ;
}
Which never made sense, since I had set cascade persist on the association in my entity and should have cascaded down automatically.
My final solution is:
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof NotificableInterface) {
if ($entity->generatesNotification()){
$notification = $this->notificationManager->generateNotificationForEntity($entity) ;
$entity->setNotification($notification) ;
// if ( $notification ) {
// $uow->persist($notification) ;
// }
$metaData = $em->getClassMetadata(get_class($entity));
$uow->recomputeSingleEntityChangeSet($metaData, $entity);
$uow->computeChangeSets();
}
}
}
}

Entity persist finds new relationship that existed before

Hi I have an action which adds people to a group.
In order to increase the usability the form for it removes the people that are already in the group.
My controller action looks like this:
public function addAction(UserGroup $userGroup)
{
$tempGroup = new UserGroup();
foreach ($userGroup->getUsers() as $user) {
$tempGroup->addUser($user);
}
$form = $this->container->get('form.factory')->create(new UserGroupQuickType(), $tempGroup);
$request = $this->container->get('request');
if ('POST' === $request->getMethod()) {
$form->submit($request);
if ($form->isValid()) {
$group = $form->getData();
/** #var $myUserManager UserManager */
$myUserManager = $this->container->get('strego_user.user_manager');
/** #var $em EntityManager */
$em = $this->container->get('em');
foreach ($group->getUsers() as $toInvite) {
$userGroup->addUser($toInvite);
}
$em->persist($userGroup);
$em->flush($userGroup);
}
}
return array(
'form' => $form->createView(),
'userGroup' => $userGroup
);
}
This code throws an exception:
A new entity was found through the relationship 'Strego\UserBundle\Entity\UserGroup#users'
that was not configured to cascade persist operations for entity: Degi.
To solve this issue: Either explicitly call EntityManager#persist() on
this unknown entity or configure cascade persist this
association in the mapping for example #ManyToOne(..,cascade={"persist"}).
The new found relation with was already there before. Meaning the user "Degi" was already in the group and is not a new entity.
I can avoid this error if I'll leave out the persist but then I'll get an exception: Entity has to be managed or scheduled for removal for single computation.
This is caused by the fact that my "usergroup" entity has the whole time a entity status of 3 (= detached)
I have used the same logic (with temp group etc.) for an entity that has 1 to 1 relationship to my usergroup and from there I can easily add people even to the group.
But not with this action, which is logically doing the exact same thing.
UPDATE:
My previous update was leading in the wrong direction. But here in comparison the (almost) same controller that works:
public function addAction(BetRound $betRound)
{
$userGroup = new UserGroup();
foreach ($betRound->getUsers() as $user) {
$userGroup->addUser($user);
}
$form = $this->createForm(new UserGroupQuickType(), $userGroup);
$request = $this->getRequest();
if ('POST' === $request->getMethod()) {
$form->submit($request);
if ($form->isValid()) {
/** #var $betRoundManager BetRoundManager */
$betRoundManager = $this->container->get('strego_tipp.betround_manager');
/** #var $myUserManager UserManager */
$myUserManager = $this->container->get('strego_user.user_manager');
$group = $form->getData();
foreach ($group->getUsers() as $toInvite) {
if (!$betRound->getUserGroup()->hasUser($toInvite)) {
$betRound->getUserGroup()->addUser($toInvite);
}
}
$this->getDoctrine()->getManager()->flush();
return $this->redirect($this->generateUrl('betround_show', array('id' => $betRound->getId())));
}
}
return array(
'form' => $form->createView(),
'betRound' => $betRound
);
}
This is expected behavior since your have relationship to, not really a new entity but unmanaged object.
While iterating you could try merging $toInvite. Don't worry, if objects you acquired via form have an appropriate identifier (#Id) value set they would be just reloaded from databases instead of marked for insertion. Newly added objects, on the other hand, will be marked.
So, before trying anything, ensure that each of old $toInvite have an ID set.
foreach ($group->getUsers() as $toInvite) {
$em->merge($toInvite);
$userGroup->addUser($toInvite);
}
// safe to do now, all of the added users are either reloaded or marked for insertion
$em->perist($usetGroup);
$em->flush($userGroup);
Hope this helps.
This is happening because $userGroup is already an entity which already exists and $toInvite is a new entity and you are trying to flush $userGroup. To make this code working you need to specify cascade={"persist"} in your entity file (Annotations or yaml whichever you prefer).
Other solution is to do the reverse and persist $toInvite
foreach ($group->getUsers() as $toInvite) {
$toInvite->setUserGroup($userGroup);
$em->persist($toInvite);
}
$em->flush();
I think the first option would be a better choice
After several attempts I found out that this issue were actually 2 issues:
1. I had a listener that updated some rows on persist even if it was not necessary.
2. The paramconverter somehow detaches the entities that are coming in. Which means that my UserGroup and all my users in it are detached. As soon as they are added again to the Usergroup, those Entities seem to be "new" for the EM. Therefore I needed to merge all users AND fix my listener that updates the "createdBy", which otherwise would have also needed to been merged.

Resources