从ZF2 Doctrine2中的更新中排除实体字段

时间:2016-05-06 11:05:11

标签: php doctrine-orm zend-framework2

我遇到需要更新Doctrine2实体并排除某些字段的情况。

使用ZF2,我有一个动作来使用Zend \ Form和验证过滤器来处理更新。特别是Dish实体有一个名为photo的blob列,必需。在更新期间,我只想在提供新文件时替换照片。

这里有实体的源代码和更新盘子的控制器动作。

  

餐具\实体\ Dish.php

<?php
namespace Dishes\Entity;

use Doctrine\ORM\Mapping as ORM;

/** @ORM\Entity **/
class Dish
{
  /**
  * @ORM\Id
  * @ORM\GeneratedValue(strategy="AUTO")
  * @ORM\Column(type="integer")
  **/
  protected $id;

  /**
  * @ORM\Column(type="string")
  */
  protected $name;

  /**
  * @ORM\Column(type="text")
  */
  protected $description;

  /**
  * @ORM\Column(type="integer")
  */
  protected $time;

  /**
  * @ORM\Column(type="integer")
  */
  protected $complexity;

  /**
  * @ORM\Column(type="blob")
  */
  protected $photo;

  /**
  * Magic getter to expose protected properties.
  *
  * @param string $property
  * @return mixed
  */
  public function __get($property)
  {
    return $this->$property;
  }

  /**
  * Magic setter to save protected properties.
  *
  * @param string $property
  * @param mixed $value
  */
  public function __set($property, $value)
  {
    $this->$property = $value;
  }
}
  

餐具\控制器\ AdminController.php

public function editDishAction()
{
  //Params from url
  $id = (int) $this->params()->fromRoute('id', 0);

  $objectManager = $this->objectManager;

  $hydrator = new DoctrineObject($objectManager, false);
  $form = new DishForm();

  $existingDish = $objectManager->find('Dishes\Entity\Dish', $id);

  if ($existingDish === NULL)
    $this->notFoundAction();

  $request = $this->getRequest();

  if ($request->isPost())
  {
    $filter = new DishFilter();
    $filter->get('photo')->setRequired(false);

    $form->setHydrator($hydrator)
      ->setObject($existingDish)
      ->setInputFilter($filter);

    $post = array_merge_recursive(
      $request->getPost()->toArray(),
      $request->getFiles()->toArray()
    );

    //Backup photo stream
    $imageData = $existingDish->photo;

    $form->setData($post);
    if ($form->isValid())
    {
      //If user upload a new image read it.
      if(!empty($existingDish->photo['tmp_name']))
        $imageData = file_get_contents($existingDish->photo['tmp_name']);

      $existingDish->photo = $imageData;

      $objectManager->flush();

      $this->redirect()->toRoute('zfcadmin/dishes');
    }
  }
  else
  {
    $data = $hydrator->extract($existingDish);
    unset($data['photo']);
    $form->setData($data);
  }

  return new ViewModel(array('form' => $form));
}

实际上我将$dish->photo属性设置为NULL,但这违反了 DB NOT NULL 约束。

如何告诉Doctrine在运行时从更新中排除特定的实体字段?

1 个答案:

答案 0 :(得分:0)

默认情况下,Doctrine将数据库级别中每列的nullable属性映射到false,因为您没有在注释中设置任何nullable标记:

/**
 * @ORM\Column(type="blob")
 */
 protected $photo;

这意味着,“照片是必需的,您无法使用空值插入或更新行的照片列。”

如果要在数据库中包含null值,请使用以下注释:

/**
 * @ORM\Column(type="blob", nullable=true)
 */
 protected $photo;

并且在它的setter中不要忘记null默认参数值:

 public function setPhoto($photo = null)
 {
    $this->photo = $photo;
 }

对于这个问题;看起来你正在为动作中的每个编辑操作设置一个新的Dish对象:

$form->setHydrator($hydrator)
     ->setObject(new Dish)
     ->setInputFilter($filter);

创建新Dish对象时这是正确的。编辑时,您必须将已保留的Dish实例设置为以下格式:

// I'm just writing to explain the flow.
// Accessing the object repository in action level is not a good practice.
// Use a DishService for example.

$id           = 32; // Grab it from route or elsewhere
$repo         = $entityManager->getRepository('Dishes\Entity\Dish');
$existingDish = $repo->findOne((int) $id); 

$form->setHydrator($hydrator)
     ->setObject($existingDish)
     ->setInputFilter($filter);

我假设这是现有Dish的编辑操作。

因此,水合器将在下次调用时正确处理已更改和未触摸的字段,因为您通过表单提供已填充的Dish实例。

我还建议从DishFilter中提取InputFilterManager,而不是在操作级别手动创建它:

// $filter = new DishFilter();
$filter = $serviceLocator->get('InputFilterManager')->get(DishFilter::class);
// Exclude the photo on validation:
$filter->setValidationGroup('name', 'description', 'time', 'complexity');

希望它有所帮助。