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
Related
I need to execute any select query using the same method.
So, the query my has to filter the selected data using one value or
more.
Filters are stored in a map that has the column name as the key and
the filtering value as its value.
So my Question is : how to add filters dynamically into Select statement?
What I tried:
1. QSqlQueryModel Class:
I could create a QSqlQueryModel object and set a query to
retrieve the whole table data but, I
could not find any functionality in this class to filter this data :
QSqlQueryModel *model = new QSqlQueryModel;
model->setQuery("SELECT * FROM employee");
2. QSqlTableModel: This class is used to view table data in a qtableView,I can use this class to read table data and then filter this data like this (I have not tried this yet):
QSqlTableModel *model = new QSqlTableModel
model->setTable("employee");
model->setEditStrategy(QSqlTableModel::OnManualSubmit);
model->select();
model->setFilter("colum5 > 10");
// I can use after that data() method to retrieve filtered data.
3. For Loop I thought about using for loop to add filters directly but, I would prefer a better way because I believe that QT offers a such service.
The method shall looks like this:
/**
* #brief getData executes sql select query.
* #param query [out] QSqlQuery query object after executing the query.
* #param queryFilters [in] map of query filters (column-name, filter-
value).
* #param table [in] table name.
* #return
*/
bool getData(QSqlQuery &query, std::map<std::string,QVariant> &queryFilters,
std::string &table){
bool status = false;
std::string queryText = "SELECT * from " + table + " WHERE ";
// I should apply filters from queryFilters map here.
return status;
}
There are several ways you could do this.
Using a for-loop with std::map.
Use a for loop to iterate through your key-pair values.
bool getData(QSqlQuery &query, const std::map<std::string,QVariant> &queryFilters,
std::string &table)
{
// ...
std::string queryText = "SELECT * from " + table + " WHERE";
for (auto it = queryFilters.begin(); it != queryFilters.end(); )
{
queryText += " " + it->first + "='" + it->second.toString().toStdString() + "'";
it++;
// check the iterator isn't the last iterator
if (it != queryFilters.end())
queryText += " AND"; // separate your "filters" using an AND
}
// ...
}
Using a for-loop with QMap.
But heck this is Qt so why not take advantage of the QMap, QStringList, and QString QTL types.
bool getData(QSqlQuery &query, const QMap<QString, QVariant> &queryFilters,
const QString &table)
{
// ...
QString queryText = "SELECT * from " + table + " WHERE ";
QStringList filters;
foreach (const QString &filterKey, queryFilters.keys())
filters << filterKey + "='" + queryFilters.value(filterKey).toString() + "'";
queryText += filters.join(" AND ");
// ...
}
Note that foreach is a Qt-defined macro. See the foreach keyword.
For other QTL types you might want to be aware of, see containers.
QSqlQueryModel???
I can't tell from your question and comments whether you actually have an sql table model/view/widget in the background or whether you're using something else entirely.
I thought about using loop for this matter.But, I thought that there is a better way using some qt classes like : QSqlQueryModel
For sure, just browsing through the documentation, QSqlQueryModel doesn't have a filter feature.
But... QSqlTableModel does have this feature. The plus side is, that if you already have a QSqlQueryModel sitting somewhere, you could upgrade it to a QSqlTableModel since the latter inherits the former. But again, I don't have enough information to make judgements so I'm just leading you around in the dark here.
Hopefully, this answer sheds some light on your predicament along with the reminder of how you could ask a better question to obtain more accurate responses.
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 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.
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'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);
}