7

저는 StofDoctrineExtensionsBundle (와 Gedmo DoctrineExtensions)에서 Symfony2.2를 사용하고 있습니다. 기존 개체의 속성을 변경할 때 나는 간단한 개체Gedmo Loggable은 변경되지 않은 데이터를 기록합니다.

/** 
* @ORM\Entity 
* @Gedmo\Loggable 
* @ORM\Table(name="person") 
*/ 
class Person { 
    /** 
    * @ORM\Id 
    * @ORM\Column(type="integer") 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    protected $id; 

[...] 

    /** 
    * @ORM\Column(type="datetime", nullable=true) 
    * @Assert\NotBlank() 
    * @Assert\Date() 
    * @Gedmo\Versioned 
    */ 
    protected $birthdate; 
} 

을했습니다, 로그 항목은 테이블 ext_log_entries에서 이루어집니다. 이 로그 테이블의 항목에는 변경된 컬럼 만 들어 있습니다. 나는하여 로그를 읽을 수 있습니다 :

$em = $this->getManager(); 
$repo = $em->getRepository('Gedmo\Loggable\Entity\LogEntry'); 
$person_repo = $em->getRepository('Acme\MainBundle\Entity\Person'); 

$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 protected]" 
    ["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 
} 

정말 변경된 데이터 만 기록하고 싶습니다. 아직 보지 못했던 옵션이 있습니까? 사실 birthdateDateTime 개체와 관련이있는 것 같습니다. 그렇습니까?

편집 DateTime 개체와 관련이 없습니다. 이것은 다른 엔티티에서도 발생합니다.

/** 
* @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; 
} 

수정없이 브라우저에서 편집 양식에게 절약을 열고, 로그 테이블이 포함되어 있습니다 :

update 2013-04-26 11:32:42  Acme\MainBundle\Entity\Application a:1:{s:16:"insurance_number";s:7:"1234567";} 
update 2013-04-26 11:33:17  Acme\MainBundle\Entity\Application a:1:{s:16:"insurance_number";s:7:"1234567";} 

이유는 다른 엔티티는 간단한 값을 포함하는거야?

답변

7

다른 확장 기능 (timestampable)을 사용할 때 이와 비슷한 문제가 발생할 수 있습니다. 즉, doctrine에 사용 된 기본 변경 추적 정책 (변경 사항 자동 감지)은 때때로 엔티티를 더티, 그렇지 않은 경우 (내 엔터티가 datetime 객체를 포함하고있을 때이 상황이 발생했습니다. 데이터베이스에서 객체를 가져올 때 생성해야하는 객체이므로이 객체가 이해할 수 있습니다). 이것은 버그 또는 아무것도 아닙니다. 예상되는 동작이며 그 주위에 몇 가지 방법이 있습니다.

로깅하고보고 싶은 엔티티에 대체 변경 추적 정책을 구현하려고하면 가치가있을 수 있습니다. 문제가 해결되면 볼 수 있습니다.이 동작 (로깅)은 엔티티 상태가 더티가 아니면,

YourBundle\Entity\YourThing: 
    type: entity 
    table: some_table 
    changeTrackingPolicy: NOTIFY 

이 스레드를 참조하십시오 :

http://docs.doctrine-project.org/en/latest/cookbook/implementing-the-notify-changetracking-policy.html

당신의 실체를 업데이트하는 것을 잊지 마세요 : 어떤 수동으로 직접 변경 내용 추적을 구현하여 피할 수

01 여기

/** 
* @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를 선언 : 나는 아직도 그것을하지만 귀하의 질문이의 두 번째 부분에 대한 작업입니다 \DateTime를 들어 23,516,

https://github.com/Atlantic18/DoctrineExtensions/issues/333#issuecomment-16738878

+0

심지어 오래된 질문이기도하다. 내 자신의 알림 기능을 구현하지 않기로 결정한이 진절머리 나는 행동을 시작했다. ( – rabudde

+1

@rabudde Doctrine의 잘못이 아니라 PHP의 진부한 객체 비교 – calumbrodie

+0

아 PHP의 잘못이기도합니다.하지만 PHP를 기반으로하는 응용 프로그램의 번들/플러그인을 작성하는 경우 객체 자체를 비교하는 것이 어려울 것이라고 상상할 수는 없지만 Gedmo 번들을 해킹하지는 않습니다. 그럼에도 불구하고 당신의 대답에 감사드립니다. – rabudde

0

Numeric 속성 내 문제를 해결하는 방법입니다 정수형 속성이지만 우리가 알고있는 것처럼 PHP은 타입이없고 동적 인 캐스팅을합니다. 이것은 Gedmo Loggable과 충돌합니다.

을 해결하려면 Setter Method 또는 Business Logic에 명시 적으로 캐스팅해야합니다.

예를 들어이 (비즈니스 로직)을 대체 :

$application->setInsuranceNumber($valueComeFromHtmlForm)

을이 하나 :

$application->setInsuranceNumber((int)$valueComeFromHtmlForm)

을 그런 다음 개체를 지속하는 경우에 당신이 어떤 기록을 볼 것이다 로그.

Loggable 또는 Doctrine Change TrackerInteger을 받고 String (which is a 'not casted Integer')을 수신하므로 더티 속성을 표시합니다. Log Record (S은 새로운 값이 String임을 나타냅니다.)

0

오늘도이 문제가 발생하여 해결되었습니다. 모든 문자열, float, int 및 DateTime 값에 대해 작업하는 완전한 솔루션입니다.

  1. 자신의 LoggableListener를 만들어 Gedmo Listener 대신 사용하십시오.

    <?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; 
        } 
    } 
    
  2. symfony 응용 프로그램의 경우 수신기를 config.yml 파일에 등록하십시오.

    stof_doctrine_extensions: 
        orm: 
         default: 
          loggable: true 
        class: 
         loggable: MyBundle\Loggable\Listener\MyLoggableListener 
    
  3. 당신이 당신의 엔티티에 날짜 시간 필드를 사용하고 있지만 데이터베이스에서 당신은 날짜를 저장하는 경우에, 당신은 모든 세터에서 시간 부분을 다시 설정해야합니다. 작업을 수행해야합니다

    public function setDateValue(DateTime $dateValue = null) 
    { 
        $dateValue->setTime(0, 0, 0); 
        $this->dateValue = $dateValue; 
        return $this; 
    } 
    

.