GedmoLoggable регистрирует данные, которые не изменились
я использую Symfony2.2 с StofDoctrineExtensionsBundle (и так Gedmo DoctrineExtensions). У меня простая сущность
/**
* @ORMEntity
* @GedmoLoggable
* @ORMTable(name="person")
*/
class Person {
/**
* @ORMId
* @ORMColumn(type="integer")
* @ORMGeneratedValue(strategy="AUTO")
*/
protected $id;
[...]
/**
* @ORMColumn(type="datetime", nullable=true)
* @AssertNotBlank()
* @AssertDate()
* @GedmoVersioned
*/
protected $birthdate;
}
при изменении атрибута для существующего объекта запись журнала выполняется в таблице ext_log_entries
. Запись в этой таблице журнала содержит только измененные столбцы. Я могу прочитать журнал на:
$em = $this->getManager();
$repo = $em->getRepository('GedmoLoggableEntityLogEntry');
$person_repo = $em->getRepository('AcmeMainBundleEntityPerson');
$person = $person_repo->find(1);
$log = $repo->findBy(array('objectId' => $person->getId()));
foreach ($log as $log_entry) { var_dump($log_entry->getData()); }
но чего я не понимаю, почему поле birthdate
всегда содержится в записи журнала, даже она не изменилась. Вот несколько примеров трех логов записи:
array(9) {
["salutation"]=>
string(4) "Herr"
["firstname"]=>
string(3) "Max"
["lastname"]=>
string(6) "Muster"
["street"]=>
string(14) "Musterstraße 1"
["zipcode"]=>
string(5) "00000"
["city"]=>
string(12) "Musterhausen"
["birthdate"]=>
object(DateTime)#655 (3) {
["date"]=>
string(19) "1893-01-01 00:00:00"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/Berlin"
}
["email"]=>
string(17) "email@example.com"
["phone"]=>
NULL
}
array(2) {
["birthdate"]=>
object(DateTime)#659 (3) {
["date"]=>
string(19) "1893-01-01 00:00:00"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/Berlin"
}
["phone"]=>
string(9) "123456789"
}
array(2) {
["birthdate"]=>
object(DateTime)#662 (3) {
["date"]=>
string(19) "1893-01-01 00:00:00"
["timezone_type"]=>
int(3)
["timezone"]=>
string(13) "Europe/Berlin"
}
["phone"]=>
NULL
}
Я хочу регистрировать только действительно измененные данные. Есть ли вариант я еще не видел? Кажется, это связано с тем, что birthdate
это DateTime
"объект", не так ли?
редактировать Это не связано с
3 ответов
Это может быть аналогичным вопросом я столкнулся при использовании другой из этих расширений (timestampable), а именно: что по умолчанию отслеживание изменений политики в учение (оно пытается автоматически обнаружить изменения) иногда знаки сущностей, как грязные, когда они не являются (для меня это происходило, когда моя сущность объект datetime, что вполне объяснимо, учитывая, что это объект, который должен быть разработан при вытягивании его из базы данных). Это не жучок или что - то еще- это ожидаемое поведение и есть несколько способов его обойти.
возможно, стоит попытаться реализовать альтернативную политику отслеживания изменений на объектах, которые вы хотите зарегистрировать, и посмотреть, исправляет ли это - я бы предположил, что это поведение (ведение журнала) не срабатывает, если состояние сущности не является грязным, чего вы можете избежать, реализуя отслеживание изменений самостоятельно вручную:
Не забудьте обновить сущность:
YourBundle\Entity\YourThing:
type: entity
table: some_table
changeTrackingPolicy: NOTIFY
смотрите здесь:
https://github.com/Atlantic18/DoctrineExtensions/issues/333#issuecomment-16738878
на \DateTime
Я все еще работаю над этим, но что касается второй части вашего вопроса, есть способ решить мою проблему с моим Numeric
свойства:
/**
* @ORM\Entity
* @Gedmo\Loggable
* @ORM\Entity(repositoryClass="Acme\MainBundle\Repository\ApplicationRepository")
* @ORM\Table(name="application")
*/
class Application {
[...]
/**
* @ORM\Column(type="integer")
* @Assert\NotBlank(groups={"FormStepOne", "UserEditApplication"})
* @Gedmo\Versioned
*/
protected $insurance_number;
}
здесь вы объявить insurance_number
как целочисленное свойство, но как мы знаем!--4--> не имеет типа и делает динамическое литье, которое конфликтует с Gedmo Loggable
.
решить, просто убедитесь, что вы делаете явное приведение себя в Setter Method
или в Business Logic
.
например, замените это (бизнес-логика):
$application->setInsuranceNumber($valueComeFromHtmlForm)
с этим:
$application->setInsuranceNumber( (int)$valueComeFromHtmlForm)
затем, когда вы сохраняете свой объект, вы не увидите никаких записей в своих журналах.
я думаю, это потому, что Loggable
или Doctrine Change Tracker
ждет Integer
и получает String (which is a 'not casted Integer')
и поэтому он помечает собственность грязной. Мы можем видеть это в Log Record
(S
обозначает, что новое значение-String.)
Я также столкнулся с этой проблемой сегодня и решил ее. вот полное решение, работает для всех значений string, float, int и DateTime.
-
сделайте свой собственный LoggableListener и используйте его вместо слушателя Gedmo.
<?php namespace MyBundle\Loggable\Listener; use Gedmo\Loggable\LoggableListener; use Gedmo\Tool\Wrapper\AbstractWrapper; class MyLoggableListener extends LoggableListener { protected function getObjectChangeSetData($ea, $object, $logEntry) { $om = $ea->getObjectManager(); $wrapped = AbstractWrapper::wrap($object, $om); $meta = $wrapped->getMetadata(); $config = $this->getConfiguration($om, $meta->name); $uow = $om->getUnitOfWork(); $values = []; foreach ($ea->getObjectChangeSet($uow, $object) as $field => $changes) { if (empty($config['versioned']) || !in_array($field, $config['versioned'])) { continue; } $oldValue = $changes[0]; if ($meta->isSingleValuedAssociation($field) && $oldValue) { if ($wrapped->isEmbeddedAssociation($field)) { $value = $this->getObjectChangeSetData($ea, $oldValue, $logEntry); } else { $oid = spl_object_hash($oldValue); $wrappedAssoc = AbstractWrapper::wrap($oldValue, $om); $oldValue = $wrappedAssoc->getIdentifier(false); if (!is_array($oldValue) && !$oldValue) { $this->pendingRelatedObjects[$oid][] = [ 'log' => $logEntry, 'field' => $field, ]; } } } $value = $changes[1]; if ($meta->isSingleValuedAssociation($field) && $value) { if ($wrapped->isEmbeddedAssociation($field)) { $value = $this->getObjectChangeSetData($ea, $value, $logEntry); } else { $oid = spl_object_hash($value); $wrappedAssoc = AbstractWrapper::wrap($value, $om); $value = $wrappedAssoc->getIdentifier(false); if (!is_array($value) && !$value) { $this->pendingRelatedObjects[$oid][] = [ 'log' => $logEntry, 'field' => $field, ]; } } } //fix for DateTime, integer and float entries if ($value == $oldValue) { continue; } $values[$field] = $value; } return $values; } }
-
для приложения Symfony зарегистрируйте прослушиватель в config.файл yml.
stof_doctrine_extensions: orm: default: loggable: true class: loggable: MyBundle\Loggable\Listener\MyLoggableListener
-
Если вы используете поля DateTime в своих сущностях, но в базе данных вы храните только дату, тогда вам также нужно сбросить часть времени во всех сеттерах.
public function setDateValue(DateTime $dateValue = null) { $dateValue->setTime(0, 0, 0); $this->dateValue = $dateValue; return $this; }
Это должно сделать работу.