2012-06-15 3 views
4

У меня есть таблица A, которая ссылается на таблицу B.доктрина картирование не найден случай

Редактировать: Используется двигатель базы данных MyISAM.

Доктрина картирование работает как шарм, кроме случаев, когда у меня есть недопустимый случай в БД, где ссылается ID в таблице А на самом деле не существует в таблице В.

Итак, когда вы выполняете этот код:

$objectB = $objectA->getObjectB();//with lazy load 

Вы фактически получаете объект объекта $ objectB, который является не имеет значения. Итак! Empty ($ objectB) пройдет.

И когда вы пытаетесь получить доступ к любому свойству $ objectB, как:

$ objectB-> GetName();

Вы получаете Объект не найден Исключением. Вы не можете предсказать в своем коде, что $ objectB на самом деле не существует и что нет свойства Name для объекта $ objectB.

$ objectB должен фактически иметь значение null, но этого не происходит.

Hibernate фактически картирование свойства не обретенные = игнорировать который устанавливает отсутствующую объект NULL вместо того, чтобы установить его в объект прокси. Есть ли у Доктрины что-то подобное?

PS. Конечно, вы всегда можете поймать сущность не найденное исключение и поиграть с этим. Или вы можете сопоставить фактическое поле objectB_ID в таблице A, но это не 100% -ные чистые решения.

Надеюсь, у кого-то есть ответ.

Спасибо

+0

Если вы ожидаете объект из геттера и возвращает нуль, может быть, вы должны проверить на нуль первым. Это немного сложнее, потому что Doctrine создает для вас объекты-прокси, но принцип тот же. – hafichuk

+0

Это точно мой вопрос. Как вы проверяете для NULL прокси-объект, который не имеет физической записи в БД? – Goran

ответ

7

кроме случаев, когда у меня есть недопустимый случай в БД, где ссылается ID в таблице А на самом деле не существует в таблице B

ИМО это случай мусора в, вывоз мусора. Если у вас есть схема, где TableA может содержать или не иметь строку в TableB, вы должны реализовать ограничение FK для TableB, так что, если строка удалена из TableB, любые строки в таблице A, ссылающиеся на удаленные строки, будут иметь свои значения, измененные на null.

Если вы действительно хотите, чтобы двигаться вперед по вашей предлагаемой реализации схемы, вы можете попробовать сделать:

$rowExists = ($objectA->getObjectB()->getId() > 0) ? true : false; 

Это, конечно, предполагает, что вы есть столбец ID на TableB и что без знака.

- Обновление -

try { 
    $objectB = $objectA->getObjectB(); 
} catch (Exception $e) { 
    $objectB = null; 
} 

if ($objectB instanceof ClassB) { 
    // Do work 
} 
+1

Если вы запустите это: $ objectA-> getObjectB() -> getId()> 0? true: false; вы получите _Entity not found_ exception throw. – Goran

+0

Еще одна вещь. Используемый движок базы данных - это MyISAM, поэтому ограничение FK в этом случае не является решением. Да, в базе данных много мусора. Но я ищу решение Doctrine для этой ситуации. – Goran

+0

Вы попробовали мое предложение? Он должен работать. –

1

Если вы посмотрите на один из ваших сгенерированных прокси-классов, вы увидите, что __load() и __clone() функции как бросить EntityNotFoundException.

Функция __load() вызывается, когда вы «лениво» вызываете функцию getName().

class ObjectB extends \Foo\Entity\ObjectB implements \Doctrine\ORM\Proxy\Proxy 
{ 
    private $_entityPersister; 
    private $_identifier; 
    public $__isInitialized__ = false; 
    public function __construct($entityPersister, $identifier) 
    { 
     $this->_entityPersister = $entityPersister; 
     $this->_identifier = $identifier; 
    } 
    /** @private */ 
    public function __load() 
    { 
     if (!$this->__isInitialized__ && $this->_entityPersister) { 
      $this->__isInitialized__ = true; 

      if (method_exists($this, "__wakeup")) { 
       // call this after __isInitialized__to avoid infinite recursion 
       // but before loading to emulate what ClassMetadata::newInstance() 
       // provides. 
       $this->__wakeup(); 
      } 

      if ($this->_entityPersister->load($this->_identifier, $this) === null) { 
       throw new \Doctrine\ORM\EntityNotFoundException(); 
      } 
      unset($this->_entityPersister, $this->_identifier); 
     } 
    } 
... 
    public function getName() 
    { 
     $this->__load(); 
     return parent::getName(); 
    } 
... 
} 

Вы в основном имеет несколько вариантов, первые из которых используются try/catch блока.

try { 
    $name = $objectB->getName(); 
} catch (\Doctrine\ORM\EntityNotFoundException $e) { 
    $name = null; 
} 

Или вы можете посмотреть на реализацию функции __wakeup() в ObjectB и, возможно, ее обработки себя (хотя вы будете более вероятно, нужно бросить исключение в любом случае).

И наконец, если вы чувствуете амбициозность, вы можете изменить шаблон Proxy. \Doctrine\ORM\Proxy\ProxyFactory содержит шаблон.

/** Proxy class code template */ 
    private static $_proxyClassTemplate = 
'<?php 

namespace <namespace>; 

/** 
* THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. 
*/ 
class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy 
{ 
    private $_entityPersister; 
    private $_identifier; 
    public $__isInitialized__ = false; 
    public function __construct($entityPersister, $identifier) 
    { 
     $this->_entityPersister = $entityPersister; 
     $this->_identifier = $identifier; 
    } 
    /** @private */ 
    public function __load() 
    { 
     if (!$this->__isInitialized__ && $this->_entityPersister) { 
      $this->__isInitialized__ = true; 

      if (method_exists($this, "__wakeup")) { 
       // call this after __isInitialized__to avoid infinite recursion 
       // but before loading to emulate what ClassMetadata::newInstance() 
      // provides. 
       $this->__wakeup(); 
      } 

      if ($this->_entityPersister->load($this->_identifier, $this) === null) { 
       throw new \Doctrine\ORM\EntityNotFoundException(); 
      } 
      unset($this->_entityPersister, $this->_identifier); 
     } 
    } 

Вы должны быть в состоянии уйти только с удалением бросания EntityNotFoundException в функциях __load() и __clone(), хотя может быть непреднамеренными побочными эффектами. Вы также, вероятно, захотите взглянуть на это изменение как на патч, если вы планируете периодически обновлять Doctrine.

+0

У нас есть старые данные из старого приложения, которые очень противоречивы. Его очень сложно очистить данные вручную. Я получаю EntityNotFoundException, если я загружаю данные в таблицу с проверкой внешнего ключа. Есть ли способ обработать это исключение и установить значение столбца внешнего ключа в NULL и изменить одно и то же исключение, если доктрина не сможет найти связанный объект, чтобы данные были автоматически очищены? – vishal

0

Используйте его в аннотации в классе Objecta:

@ORM\ManyToOne(targetEntity="ObjectB", fetch="EAGER") 
0

Мы имели эту проблему с помощью Doctrine ORM, где объект А был достаточно важным, что мы не хотим, чтобы просто удалить его с помощью какой-то скрипт очистки, если Объект A больше не существует. Это не произойдет во время обычного выполнения приложения, но очень редко при ручном редактировании таблиц базы данных, например во время миграции/обновления.

Несколько вариантов мы рассмотрели, но не были слишком увлечены:

  • Использования поставщика конкретных исправлений, в этом случае ON DELETE SET NULL ограничения для SQL Server).
  • Использование пользовательского расширения Twig, чтобы поймать EntityNotFoundException (мы действительно испытывали эту проблему только в шаблонах Twig, но не хотели, чтобы они добавляли расширение повсюду, а затем все еще потенциально имели проблемы в наших PHP-контроллерах).

Вместо этого мы решили немного загрязнить наши объекты, поймав EntityNotFoundException внутри них, но включив всю логику в свойство EntityExistanceCheckableTrait.

Как только объект добавлен как к объекту А, так и к объекту B, нам нужно было только позвонить $objectB->hasObjectB() или {{ objectB.hasObjectA() }} в Twig и обработать его, как это продиктовано логикой контроллера/шаблона.

class ObjectA 
{ 
    use EntityExistanceCheckableTrait; 

    ... 
} 

и

class ObjectB 
{ 
    use EntityExistanceCheckableTrait; 

    ... 

    public function hasObjectB() 
    { 
     return $this->hasEntity('ObjectB'); 
    } 
} 

Черта добавляет PHP метод волшебной __invoke(), который просто возвращает истину, если она существует, но это все, что нам нужно сделать нагрузку прокси и посмотреть, если соответствующий объект на самом деле eixsts или просто сиротский идентификатор в ассоциированном объекте.

Вот полный код признака:

/** 
* Add to the entities on both sides of a Doctrine Association then implement a wrapper around hasEntity() in the 
* owning entity. 
* 
* Trait EntityExistanceCheckableTrait 
*/ 
trait EntityExistanceCheckableTrait 
{ 
    /** 
    * This can be empty but needs to be defined so we can check that the entity is callable. 
    * 
    * @return bool 
    */ 
    public function __invoke() 
    { 
     return true; 
    } 

    /** 
    * @param string $entityName 
    * @return bool 
    */ 
    public function hasEntity($entityName) 
    { 
     // Create the callable method 
     $entityMethod = 'get'.$entityName; 

     try { 
      $entity = $this->$entityMethod(); 

      // We invoke the associated entity to force the real entity to be lazy-loaded instead of the proxy. 
      // This way we can ensure that we don't have an orphan entity (e.g. a proxy with the ID of the associated 
      // entity, loaded from the original entity, but the associated entity no longer exists in the database). 
      return (isset($entity) && $entity()) ? true : false; 
     } catch (EntityNotFoundException $e) { 
      return false; 
     } 
    } 
} 
Смежные вопросы