Сбой Doctrine L2 и проверка Symfony - PullRequest
0 голосов
/ 17 июня 2019

Недавно я начал использовать компонент Symfony Validator в своем скрипте импортера, чтобы проверить, установлены ли все необходимые поля для сущности и нет ли «уникальных нарушений ограничений».Неверные записи пропускаются и регистрируются.

Я не уверен, но эта проблема GitHub может описать ту же проблему: https://github.com/doctrine/orm/issues/7277

  • компонент проверки имеет версию v4.2.5

  • и библиотека доктрины имеет версию v2.6.3

У кого-нибудь была такая проблема раньше?Как мне решить эту проблему?

error: {
    message: "Argument 2 passed to Doctrine\ORM\Cache\EntityCacheKey::__construct() must be of the type array, null given, called in /var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php on line 353",
    trace: [
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/EntityCacheKey.php:49",
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php:353",
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php:305",
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php:426",
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php:181",
        "/var/www/myproject/vendor/symfony/doctrine-bridge/Validator/Constraints/UniqueEntityValidator.php:139",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveContextualValidator.php:809",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveContextualValidator.php:525",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveContextualValidator.php:330",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveContextualValidator.php:141",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveValidator.php:100",
        "/var/www/myproject/src/Service/Api/Import/StoreImporter.php:251",
        "/var/www/myproject/src/Command/Api/Import/StoreImporterCommand.php:92",
        "/var/www/myproject/vendor/symfony/console/Command/Command.php:255",
        "/var/www/myproject/vendor/symfony/console/Application.php:926",
        "/var/www/myproject/vendor/symfony/framework-bundle/Console/Application.php:89",
        "/var/www/myproject/vendor/symfony/console/Application.php:269",
        "/var/www/myproject/vendor/symfony/framework-bundle/Console/Application.php:75",
        "/var/www/myproject/vendor/symfony/console/Application.php:145",
        "/var/www/myproject/bin/console:39"
    ]
},

В другом Importer возникает проблема, которая также связана с кэшем второго уровня.Трассировка стека показывает одно отличие, она использует liC / functions-test-bundle DataCollectingValidator, потому что это когда я запускал команду в среде 'dev'.

error: {
    message: "Notice: Undefined index: 0000000051f7928f000000005709f6ca",
    trace: [
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:2995",
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php:352",
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/DefaultQueryCache.php:305",
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php:426",
        "/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php:181",
        "/var/www/myproject/vendor/symfony/doctrine-bridge/Validator/Constraints/UniqueEntityValidator.php:139",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveContextualValidator.php:809",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveContextualValidator.php:525",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveContextualValidator.php:330",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveContextualValidator.php:141",
        "/var/www/myproject/vendor/symfony/validator/Validator/RecursiveValidator.php:100",
        "/var/www/myproject/vendor/symfony/validator/Validator/TraceableValidator.php:66",
        "/var/www/myproject/vendor/liip/functional-test-bundle/src/Validator/DataCollectingValidator.php:66",
        "/var/www/myproject/src/Service/Api/Import/DeviceImporter.php:272",
        "/var/www/myproject/src/Command/Api/Import/DeviceImporterCommand.php:92",
        "/var/www/myproject/vendor/symfony/console/Command/Command.php:255",
        "/var/www/myproject/vendor/symfony/console/Application.php:926",
        "/var/www/myproject/vendor/symfony/framework-bundle/Console/Application.php:89",
        "/var/www/myproject/vendor/symfony/console/Application.php:269",
        "/var/www/myproject/vendor/symfony/framework-bundle/Console/Application.php:75",
        "/var/www/myproject/vendor/symfony/console/Application.php:145",
        "/var/www/myproject/bin/console:39"
    ]
}

edit: добавлена ​​конфигурация Unique EntityОграничения:

/**
 * Device Entity
 * 
 * @ORM\Cache(region = "rarely_changing", usage = "NONSTRICT_READ_WRITE")
 * @ORM\Entity(repositoryClass = "App\Repository\DeviceRepository")
 * @ORM\Table(name = "s4_device", indexes = {
 *     @ORM\Index(name = "device_model_search_index", columns = {"device_model"}),
 *     @ORM\Index(name = "device_slug_search_index", columns = {"device_slug"}),
 * }, uniqueConstraints = {
 *     @ORM\UniqueConstraint(name = "UNIQUE_DEVICE_MODEL_AND_BRAND", columns = {"device_model", "brand_id"})
 * })
 * @UniqueEntity(fields = {"model", "brand"}, errorPath = "model", message = "device-model-is-not-unique")
 */
class Device extends AbstractEntity
{
    /* ... */

    /**
     * @var DeviceReference
     *
     * @ORM\Cache(region = "rarely_changing", usage = "NONSTRICT_READ_WRITE")
     * @ORM\OneToOne(targetEntity = "App\Entity\Model\Reference\DeviceReference", mappedBy = "device", cascade = {"persist", "remove"})
     */
    protected $reference;

    /* ... */
}
/**
 * Store Entity
 *
 * @ORM\Cache(region = "rarely_changing", usage = "NONSTRICT_READ_WRITE")
 * @ORM\Entity(repositoryClass = "App\Repository\StoreRepository")
 * @ORM\Table(name = "s4_store", indexes = {
 *     @ORM\Index(name = "store_name_search_index", columns = {"store_name"}),
 *     @ORM\Index(name = "store_slug_search_index", columns = {"store_slug"})
 * }, uniqueConstraints = {
 *     @ORM\UniqueConstraint(name = "UNIQUE_STORE_NAME", columns = {"store_name"})
 * })
 * @UniqueEntity(fields = {"name"}, message = "store-name-is-not-unique")
 * @UniqueEntity(fields = {"uri"}, message = "store-uri-is-not-unique")
 */
class Store extends AbstractEntity
{
    /* ... */

    /**
     * @var StoreReference|null
     *
     * @ORM\Cache(region = "rarely_changing", usage = "NONSTRICT_READ_WRITE")
     * @ORM\OneToOne(targetEntity = "App\Entity\Model\Reference\StoreReference", mappedBy = "store", cascade = {"persist", "remove"})
     */
    protected $reference;

    /* ... */
}

Store Importer (пример, где несвязанный код опущен)

namespace App\Service\Api\Import;

/* ... */

use Symfony\Component\Validator\Validator\ValidatorInterface;

class StoreImporter
{
    /**
     * @var StoreManagerInterface
     */
    private $_storeManager;

    /**
     * @var ValidatorInterface
     */
    private $_validator;

    /**
     * @param StoreManagerInterface    $storeManager
     * @param ValidatorInterface $validator
     */
    public function __construct(StoreManagerInterface $storeManager, ValidatorInterface $validator)
    {
        $this->_storeManager = $storeManager;
        $this->_validator = $validator;
    }

    public function import()
    {
        /* ... */

        foreach ($storeHashes as $md5hash => $storeDetails) {

            /* @var Store $store */
            $store = $this->_storeManager->findOrCreateByReference(
                $storeDetails["company_id"], // cs-cart ID
                $storeDetails["company"]     // store name
            );

            /* ... */

            $validationErrors = $this->_validator->validate($store);

            if (count($validationErrors) > 0) {

                $validationContext = [
                    "errors"  => (string) $validationErrors,
                    "details" => $storeDetails,
                ];

                $this->logError("Skipping " . (string) $store . " - Store has validation errors", $validationContext);

                continue; // skip saving store details when there are validation errors
            }

            /* ... */

            $this->_storeManager->saveOne($store, false);

        }

        /* ... */

        $this->_storeManager->flush();
        $this->_storeManager->clear();

        /* ... */
    }

    /* ... */
}

Edit # 2: это то, что происходит в строке 139 средства проверки уникальных сущностей.

138
139 $result = $repository->{$constraint->repositoryMethod}($criteria);
140

сброшенные переменные ($ constraint и $ критерии)

"constraint" => Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity {#2                                462
    +message: "device-model-is-not-unique"
    +service: "doctrine.orm.validator.unique"
    +em: null
    +entityClass: null
    +repositoryMethod: "findBy"
    +fields: array:2 [
      0 => "model"
      1 => "brand"
    ]
    +errorPath: "model"
    +ignoreNull: true
    +payload: null
    +"groups": array:2 [
      0 => "Default"
      1 => "Device"
    ]
  }
"criteria" => array:2 [
    "model" => "Xperia XZ"
    "brand" => App\Entity\Model\DeviceBrand {#2183
      #type: "device_brand"
      #devices: Doctrine\ORM\PersistentCollection {#2184
        -snapshot: []
        -owner: App\Entity\Model\DeviceBrand {#2183}
        -association: array:16 [ …16]
        -em: Doctrine\ORM\EntityManager {#561 …11}
        -backRefFieldName: "brand"
        -typeClass: Doctrine\ORM\Mapping\ClassMetadata {#128 …}
        -isDirty: false
        #collection: Doctrine\Common\Collections\ArrayCollection {#2185
          -elements: []
        }
        #initialized: false
      }
      #id: 14
      #name: "Sony"
      #slug: "sony"
      #createdAt: DateTime @1560754604 {#2182
        date: 2019-06-17 08:56:44.0 Europe/Berlin (+02:00)
      }
      #createdBy: null
      #deleted: false
      #deletedAt: null
      #deletedBy: null
      #updatedAt: null
      #updatedBy: null
    }
  ]

Edit # 3:

Я обнаружил немного странного поведения, пытаясьотладка класса UnitOfWork в библиотеке Doctrine ORM.Очевидно, что хеш объекта не может быть найден в массиве entityIdentifiers.Тип сущности, которая не может быть найдена, является отношением OneToOne к обновляемой сущности.

UnitOfWork (с модификацией)

    /**
     * Gets the identifier of an entity.
     * The returned value is always an array of identifier values. If the entity
     * has a composite identifier then the identifier values are in the same
     * order as the identifier field names as returned by ClassMetadata#getIdentifierFieldNames().
     *
     * @param object $entity
     *
     * @return array The identifier values.
     */
    public function getEntityIdentifier($entity)
    {
        if (!array_key_exists(spl_object_hash($entity), $this->entityIdentifiers)) {
            dump([
                "entity-class" => get_class($entity),
                "object-hash"  => spl_object_hash($entity),
                "identifiers"  => $this->entityIdentifiers,
            ]);
            exit();
        }

        return $this->entityIdentifiers[spl_object_hash($entity)];
    }

результат дампа:

array:3 [
  "entity-class" => "App\Entity\Model\Reference\StoreReference"
  "object-hash" => "000000002f4c81ef00000000678832e6"
  "identifiers" => array:6 [
    "000000002f4c805b00000000678832e6" => array:1 [
      "id" => 1
    ]
    "000000002f4c805700000000678832e6" => array:1 [
      "id" => 2
    ]
    "000000002f4c813600000000678832e6" => array:1 [
      "id" => 6
    ]
    "000000002f4c816100000000678832e6" => array:1 [
      "id" => 2
    ]
    "000000002f4c819f00000000678832e6" => array:1 [
      "id" => 1
    ]
    "000000002f4c81e200000000678832e6" => array:1 [
      "id" => 1
    ]
  ]
]

Store Reference Entity


namespace App\Entity\Model\Reference;

use App\Entity\Model\Store;
use Doctrine\ORM\Mapping as ORM;

use App\Entity\Model\Reference\AbstractReferenceEntity;

/**
 * @ORM\Cache(region = "rarely_changing", usage = "NONSTRICT_READ_WRITE")
 * @ORM\Entity()
 * @ORM\Table(name = "s4_store_reference", indexes = {
 *     @ORM\Index(name = "reference_external_id_search_index", columns = {"external_id"})
 * })
 */
class StoreReference extends AbstractReferenceEntity
{
    /**
     * @return string
     */
    public function __toString()
    {
        return "[" . $this->id . "] StoreReference";
    }

    /**
     * ID
     *
     * @var integer
     *
     * @ORM\Id
     * @ORM\Column(name = "store_reference_id", type = "integer", unique = true)
     * @ORM\GeneratedValue(strategy = "AUTO")
     */
    protected $id;

    /**
     * @var Store
     *
     * @ORM\Cache(region = "rarely_changing", usage = "NONSTRICT_READ_WRITE")
     * @ORM\OneToOne(targetEntity = "App\Entity\Model\Store", inversedBy = "reference", cascade = { "persist", "remove" })
     * @ORM\JoinColumn(name = "store_id", referencedColumnName = "store_id", nullable = false)
     */
    protected $store;

    /* ... additional methods ... */
}

Я также пытаюсь воспроизвести эту проблему в этом репозитории github

1 Ответ

0 голосов
/ 27 июня 2019

Итак, я нашел решение этой проблемы, которое является ошибкой Doctrine. Это довольно нетрадиционно, но работает до тех пор, пока не выйдет версия Doctrine ORM 2.6.4.

Я настроил все ошибочные ассоциации OneToOne по-разному. изменив отношение на отношение ManyToOne и запрограммированно сохраняя и выбрав только одну сущность, у меня все еще остается отношение «один к одному», и мое приложение будет работать без исключений. код:

Устройство устройства:

    /**
     * Device References
     *
     * One Device has one Reference
     *
     * note: this relation is configured as a One to Many association to work around a bug in Doctrine
     *
     * @var ArrayCollection|DeviceReference[]
     *
     * @ORM\Cache(region = "rarely_changing", usage = "NONSTRICT_READ_WRITE")
     * @ORM\OneToMany(targetEntity = "App\Entity\Model\Reference\DeviceReference", mappedBy = "device", cascade = {"persist", "remove"})
     */
    protected $references;

    /**
     * Add reference
     *
     * @param DeviceReference $reference
     *
     * @return self
     */
    public function addReference(DeviceReference $reference)
    {
        $reference->setDevice($this);

        $this->references = new ArrayCollection([$reference]);

        return $this;
    }

    /**
     * Remove reference
     *
     * @param DeviceReference $reference
     */
    public function removeReference(DeviceReference $reference)
    {
        $this->references->removeElement($reference);
    }

    /**
     * Get references
     *
     * @return Collection|DeviceReference[]
     */
    public function getReferences()
    {
        return $this->references;
    }

    /**
     * Set reference
     *
     * @param DeviceReference $reference
     *
     * @return self
     */
    public function setReference(DeviceReference $reference)
    {
        $this->addReference($reference);

        return $this;
    }

    /**
     * Get reference
     *
     * @return DeviceReference
     */
    public function getReference()
    {
        if (0 === $this->references->count()) {
            return null;
        }

        return $this->references->first();
    }

DeviceReference объект:


    /**
     * Device
     *
     * One Reference has one Device
     *
     * note: this relation is configured as a Many to One association to work around a bug in Doctrine
     *
     * @var Device
     *
     * @ORM\Cache(region = "commonly_changing", usage = "NONSTRICT_READ_WRITE")
     * @ORM\ManyToOne(targetEntity = "App\Entity\Model\Device", inversedBy = "references")
     * @ORM\JoinColumn(name = "device_id", referencedColumnName = "device_id", nullable = false, unique = true)
     */
    private $device;

    /* ... getter and setter remain the same ... */

Не забудьте обновить любые пользовательские запросы в вашем репозитории!

...