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.