简体   繁体   中英

Exclude entity field from update in ZF2 Doctrine2

I'm in a situation that need to update a Doctrine2 Entity and exclude some fields.

With ZF2 i have an action to handle update using Zend\\Form and validation filter. In particular Dish Entity have a blob column called photo that is required . During an update i want to replace the photo only if a new file is provided.

Here there are the source code for the entity and the controller action that update dish.

Dishes\\Entity\\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;
  }
}

Dishes\\Controller\\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));
}

Actually i set $dish->photo property to NULL but this violate DB NOT NULL constraint.

How can I tell Doctrine to exclude a particular entity field from update at runtime?

Doctrine maps every column's nullable property in database level to false by default since you don't set any nullable flag in your annotation:

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

This means, "Photo is required, you can't insert or update row's photo column with a null value".

If you want to have null values in your database, use the following annotation:

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

and in it's setter don't forget the null default argument value:

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

For the question; seems like you're setting a new Dish object on every edit operation in the action:

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

This is correct when creating new Dish objects. When editing, you have to set an already persisted Dish instance to the form:

// 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);

I'm assuming this is edit action for an existing Dish.

So, the hydrator will correctly handle both changed and untouched fields on next call since you give an already populated Dish instance via the form.

I also recommend fetching the DishFilter from the InputFilterManager instead of creating it manually in action level:

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

Hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM