批量导入时,如何填写多对一关系字段?

When batch importing, how do I fill in the field of a ManyToOne relationship?

我正在尝试导入一批记录(账单),但我(与客户)存在多对一关系。 如何填写该关系的数据库列?

我在实体内部使用类似 fromArray 的函数,我在其中传递字段列表和一条记录的值(数据源是 CSV)。然后在该函数内,我只需将每个列值分配给相应的 属性。但是,属性 客户是一个对象,因此我需要传入一个我没有的对象。

我考虑过将实体管理器注入我的实体,但这被认为是一种糟糕的做法,所以我有点卡住了。

我也尝试添加一个额外的 属性 customerId 希望它能强制写入值,但它似乎坚持 属性 值之上的关系值。

这是我的代码:

class Bill

    /**
     * @var string
     *
     * @ORM\Column(name="docId", type="string", length=25, nullable=false)
     * @ORM\Id
     */
    private $id;

    /**
     * @var float|null
     *
     * @ORM\Column(name="amount", type="float", precision=10, scale=0, nullable=true)
     */
    private $amount;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Customer", inversedBy="bills")
     * @ORM\JoinColumn(name="customerId", referencedColumnName="customerId", nullable=false)
     */
    private $customer;

    public function getCustomer(): ?Customer
    {
        return $this->customer;
    }

    public function setCustomer( $customer): self
    {
        $this->customer = $customer;

        return $this;
    }

    //this is where we import
    public static function fromCSVRecord($header, $line)
    {
        $object = new self;
        foreach($header as $key => $field){
            try{
                switch($field){
                    case 'customerId':
                        //get the customer here from EM but we don't have EM
                        $customer = $em->getRepository(Customer::class)->find($line[$key]);
                        $object->setCustomer($customer);
                        break;
                    default:
                        $object->$field = $line[$key];
                        break;
                }
            }catch(\Exception $e){
                dd($line[$key]);
            }

        }

        return $object;
    }
}

我希望有一种简单的方法可以使用 ORM 导入记录值,而不必将实体管理器注入实体 class。

你的问题是你试图把这个责任推给错误的人class。你需要实体内部的实体管理器的巨大代码味道,你正确地感知,正在暗示你这是执行该操作的错误位置。

将其移至存储库。无论如何,这是一个更合乎逻辑的地方来处理这个问题,而且您已经有了可用的实体管理器。

class BillRepository extends ServiceEntityRepository
{
  //..

  public function addFromCSVRecord($header, $line) {

    $em   = $this->getEntityManager();
    $bill = new Bill();

    foreach ($header as $key => $field) {
         try {
              switch ($field) {
                 case 'customerId':
                     $customer = $em->getRepository(Customer::class)->find($line[$key]);
                      // alternatively, if you are certain of the incoming data and you do not want to hit the DB...
                      // $customer = $this->getEntityManager()->getReference(Customer:class, $line[$key]);
                      $bill->setCustomer($customer);
                      break;
                 default:
                      $bill->$field = $line[$key];
                      break;
                 }
             } catch (\Exception $e) { dd($line[$key]); }
        }
        // return $object;
        $em->persist($bill);
        $em->flush();
    }
} 

我将保留在您的方法中找到的大部分逻辑,因为我不知道具体细节。尽管更改了 return 以实际保留数据,因此对 addFromCsvRecord() 的一次调用将在数据库中创建并保留您的新对象。

请注意,在我的回答中,我向您展示了如何使用 EntityManager::getReference() 为您的客户生成对象引用。如果您可以信任输入文件,这会稍微快一些,因为您不需要为该对象访问数据库。