I have these two doctrine entity classes;
/** #ORM\Entity
* #ORM\Table(name="computer")
*/
class Computer extends Hardware
{
/**
* #ORM\OneToMany(targetEntity="LogicalDisk", mappedBy="computer", cascade={"persist", "remove"})
*/
protected $logicalDisks;
public function __construct()
{
$this->logicalDisks = new ArrayCollection();
}
public function addLogicalDisk($logicalDisk)
{
$this->logicalDisks[] = $logicalDisk;
$logicalDisk->setComputer($this);
}
public function clearLogicalDisks()
{
$this->logicalDisks->clear();
}
}
/** #ORM\Entity
* #ORM\Table(name="logical_disk")
*/
class LogicalDisk
{
/**
* #ORM\ManyToOne(targetEntity="Computer", inversedBy="logicalDisks")
* #ORM\JoinColumn(name="computer_id", referencedColumnName="id")
*/
protected $computer;
}
I'm trying to clear the existing collection, and then build up a new collection. However the old entities never get removed from the database.
$disks = $WbemServices->ExecQuery("Select * from Win32_LogicalDisk");
// Delete existing disk info for this host.
$computer = $service->findByDNSName($host, $domain);
$computer->clearLogicalDisks();
// loop through scanned disks and persist each one
foreach ($disks as $disk) {
$logicalDisk = new LogicalDisk();
$logicalDisk->setDeviceId($disk->DeviceId)
->setDescription($disk->Description)
->setFileSystem($disk->FileSystem)
->setCapacity($disk->Size)
->setFreeSpace($disk->FreeSpace);
$computer->addLogicalDisk($logicalDisk);
}
$service->persist($computer);
I have tried setting cascade={"persist", "remove"} on the logicalDisk property of the Computer entity, but this does not remove the items. I've also tried persisting the computer after clearing the ArrayCollection but the disk entities still do not get removed from the database.
I just need to know how to clear the ArrayCollection and then persist that change to the database, thus removing the unwanted entities.
Related
I use doctrine with Slim V3, I want to have PostPersist / PostUpdate function
this is my entity
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
*
* #ORM\Table(name="xxxx", indexes={#ORM\Index(name="xxxx", columns={"xxxx"})})
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class MyEntity
{
....
/**
* Set lastUpdate
*
* #param \DateTime $lastUpdate
*
*
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function setLastUpdate($lastUpdate)
{
$this->last_update = new \DateTime('now');
return $this;
}
But When I persist the entity, my field remains Null
If you want to change the database value, you are listening for the wrong event. Regarding to the doc:
postPersist - The postPersist event occurs for an entity after the entity has been made persistent. It will be invoked after the database
insert operations. Generated primary key values are available in the
postPersist event.
postUpdate - The postUpdate event occurs after the database update operations to entity data. It is not called for a DQL UPDATE
statement.
So better you handle the pre event instead of the post event. As example:
/**
*
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function setLastUpdate()
{
$this->last_update = new \DateTime('now');
return $this;
}
Hope this help
Installing and using SoftDeleteable behavior extension for Doctrine 2 is quite easy. The problem usually is trying to disable it for some code part and enabling again. You may want to do this to:
load entity that is soft-deleted
remove entity from database entirely bypassing soft-delete filter
So how to disable it?
1. How to load soft-deleted entity
As per the documentation, disable filter for entity manager:
$em->getFilters()->disable('softdeleteable');
$object = $em->find('AppBundle:Object', 1); // soft-deleted entity will be loaded
To enable soft-delete again:
$em->getFilters()->enable('softdeleteable');
Note: $em->clear(); may be required before this line, if entity was already loaded with disabled soft-delete filter.
2. How to remove entity from database entirely
Even though it is not mentioned in documentation, the first solution does not work if you need to remove entity and bypass soft-delete filter. Filter needs to be removed from entity manager's event listeners:
// initiate an array for the removed listeners
$originalEventListeners = [];
// cycle through all registered event listeners
foreach ($em->getEventManager()->getListeners() as $eventName => $listeners) {
foreach ($listeners as $listener) {
if ($listener instanceof \Gedmo\SoftDeleteable\SoftDeleteableListener) {
// store the event listener, that gets removed
$originalEventListeners[$eventName] = $listener;
// remove the SoftDeletableSubscriber event listener
$em->getEventManager()->removeEventListener($eventName, $listener);
}
}
}
// remove the entity
$em->remove($object);
$em->flush($object); // or $em->flush();
// re-add the removed listener back to the event-manager
foreach ($originalEventListeners as $eventName => $listener) {
$em->getEventManager()->addEventListener($eventName, $listener);
}
References:
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/softdeleteable.md
Force delete doctrine entity when using SoftDeletable by KnpLabs
Disable Soft Deleteable filter for hard delete record doesn't work
You can use a service to disable and reenable the soft delete filter behaviour:
<?php
namespace App\Util;
use Doctrine\ORM\EntityManagerInterface;
use Gedmo\SoftDeleteable\SoftDeleteableListener;
class SoftDeleteFilter
{
/**
* #var string
*/
const EVENT_NAME = 'onFlush';
/**
* #var object
*/
private $originalEventListener;
/**
* #param EntityManagerInterface $em
*/
public function removeSoftDeleteFilter(EntityManagerInterface $em)
{
foreach ($em->getEventManager()->getListeners() as $eventName => $listeners) {
foreach ($listeners as $listener) {
if ($listener instanceof SoftDeleteableListener) {
if ($eventName === self::EVENT_NAME) {
$this->originalEventListener = $listener;
$em->getEventManager()->removeEventListener($eventName, $listener);
}
}
}
}
}
/**
* #param EntityManagerInterface $em
*/
public function undoRemoveSoftDeleteFilter(EntityManagerInterface $em)
{
if (empty($this->originalEventListener)) {
throw new \Exception('can not undo remove, soft delete listener was not removed');
}
// re-add the removed listener back to the event-manager
$em->getEventManager()->addEventListener(self::EVENT_NAME, $this->originalEventListener);
}
}
usage:
$this->softDeleteFilter->removeSoftDeleteFilter($this->entityManager);
$this->entityManager->remove($entity);
$this->entityManager->flush();
$this->softDeleteFilter->undoRemoveSoftDeleteFilter($this->entityManager);
Just a small reminder.
When you want to hard delete entity with Gedmo Softdeletable you have to have hardDelete=true in the respective annotation, see:
#Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=true)
EDIT: hardDelete=true is true by default
With this, you dont have to disable the listener/filter. If you have hardDelete=false, the double remove suggested above will not work.
Source:
https://github.com/Atlantic18/DoctrineExtensions/blob/v2.4.x/doc/softdeleteable.md
As in a former comment by qooplmao posted: A simple and working solution is:
// Remove an entity entirely from the DB (skip soft delete)
$this->entityManager->remove($entity);
$this->entityManager->flush();
// Just run it a second time :-)
$this->entityManager->remove($entity);
$this->entityManager->flush();
Just posted it again to give it a little bot more visibility as it works like a charme...
I'm using DoctrineFixturesBundle to create test records before each test is executed. But it created a mess in my database, so I want to remove those added records after each test is finished. How can I do this without removing the whole database?
/**
* Function initializing test environment
*/
protected function setUp()
{
//
// set the client object for easier access (and cleaner code)
//
$this->client = static::createClient();
//
// create database fixtures
//
$container = $this->client->getContainer();
$doctrine = $container->get('doctrine');
$entityManager = $doctrine->getManager();
$userFixture = new LoadUsersData();
$userFixture->load($entityManager);
}
/**
* Function that runs when the testing is finished
*/
public function tearDown()
{
parent::tearDown();
//
// purge database fixtures
//
//
// close database connection
//
$this->em->close();
}
I am using ActionBarSherlock to provide ActionBars for pre HoneyComb devices.
My Activity has four fragments namely 1. User 2. Chat 3. Video 4. Extra, see image
I have created actionBar using following code:-
actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setTitle("Meeting");
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowCustomEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);
/* Set Custom view */
ActionBar.Tab tab = actionBar.newTab();
// tab.setText("Meeting Users");
tab.setIcon(R.drawable.users);
tab.setTabListener(this);
actionBar.addTab(tab);
tab = actionBar.newTab();
// tab.setText("Chat");
tab.setIcon(R.drawable.chat);
tab.setTabListener(this);
actionBar.addTab(tab);
tab = actionBar.newTab();
// tab.setText("Video");
tab.setIcon(R.drawable.video_call);
tab.setTabListener(this);
tab.select();
actionBar.addTab(tab);
tab = actionBar.newTab();
// tab.setText("Extra");
tab.setIcon(R.drawable.extra);
tab.setTabListener(this);
actionBar.addTab(tab);
I want to draw something on those tabs, for example draw and/OR blink on chat tab, whenever chat messages arrives and user is on some other tab.
How can I do this ? please help.
Use custom view for your tabs
ActionBar.Tab tab = getSupportActionBar().newTab();
tab.setCustomView(R.layout.custom_tab_view);
Then you can get views on your custom layout and make blinking
This is How I solved my problem, Hope it can be useful for someone else too....
First I created a CutomImageView by extending ImageView
package com.myexample.newsessionwindowsrbrdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.renderscript.Font.Style;
import android.widget.ImageView;
public class CustomImageView extends ImageView {
private int notificationCount;
/**
* #param context
*/
public CustomImageView(Context context) {
super(context);
notificationCount = 0;
}
public synchronized void incrementNotification() {
notificationCount--;
this.invalidate();
}
public synchronized void decrementNotification() {
notificationCount++;
this.invalidate();
}
/**
* #return the notificationCount
*/
public synchronized int getNotificationCount() {
return notificationCount;
}
/**
* #param notificationCount
* the notificationCount to set
*/
public synchronized void setNotificationCount(int notificationCount) {
this.notificationCount = notificationCount;
this.invalidate();
}
/*
* (non-Javadoc)
*
* #see android.widget.ImageView#onDraw(android.graphics.Canvas)
*/
#Override
protected void onDraw(Canvas canvas) {
System.out.println("OnDraw is called");
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
paint.setFakeBoldText(true);
paint.setTextSize(15);
canvas.drawText(String.valueOf(notificationCount), 15, 20, paint);
}
}
Then While creating tabs, I used this image as
/* Set Custom view */
mView = new CustomImageView(this);
mView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
mView.setBackgroundResource(R.drawable.users);
ActionBar.Tab tab = actionBar.newTab();
tab.setCustomView(mView);
tab.setTabListener(this);
actionBar.addTab(tab);
Now whenever the notification changes I call increment/decrement or setter method of CustomImageView and new Notifications are displayed on the image..
Suggestions to improve this solution are really welcome...
I'm using QTableView in order to display the results of QSqlQueryModel. The data in DB is permanently changed so I run the same script every time and need to get updated data. The query is executed in another thread after which it returns the result to main thread.
void SqlThread::setNewScript(QString script)
{
QSqlQueryModel * sqlModel = new QSqlQueryModel();
this->script = script;
QSqlQuery query = QSqlQuery(this->script, db);
sqlModel->setQuery(query);
emit queryFinished(sqlModel);
}
void myTable::onQueryFinished(QSqlQueryModel * model)
{
QAbstractItemModel * oldModel = this->table->model();
QSortFilterProxyModel * sort = new QSortFilterProxyModel();
sort->setSourceModel(model);
this->table->setModel(sort);
delete oldModel;
}
The problem appeared when I've tried to introduce sorting using QSortFilterProxyModel. Since I did it my table haven't received any updated data.
I checked that QSqlQueryModel doesn't receive any updated data while running the same script in DBMS gives me new results.
If I don't use QSortFilterProxyModel the table is updated normally.
I dont know the rest of your code, but this may help.
void SqlThread::setNewScript(QString script)
{
//QSqlQueryModel * sqlModel = new QSqlQueryModel();
//It's better to implement your model as [QSortFilterSqlQueryModel][1]
QSortFilterSqlQueryModel * sqlModel = new QSortFilterSqlQueryModel();
this->script = script;
QSqlQuery query = QSqlQuery(this->script, db);
sqlModel->setQuery(query);
//use select to start query
sqlModel->select();
emit queryFinished(sqlModel);
}
/*
void myTable::onQueryFinished(QSqlQueryModel * model)
{
QAbstractItemModel * oldModel = this->table->model();
QSortFilterProxyModel * sort = new QSortFilterProxyModel();
sort->setSourceModel(model);
this->table->setModel(sort);
delete oldModel;
}
rest of can be corrected like that if you really wanna pass model to
the slot(this does not seems to be good idea as your model is already on the heap)*/
void myTable::onQueryFinished(QSortFilterSqlQueryModel * model)
{
table->setModel(model)
table->setSelectionMode(QAbstractItemView::SingleSelection);//other option(s) you like
table->setSortingEnabled(true);
}