I'm pulling a base entity out of the database, cloning it inside a loop to create new entities, changing some details and then persisting the entity.
For some reason this is throwing No Entity in Identity Map in addToIdentityMap in UnitOfWork when calling EntityManager->flush().
I've also tried creating an entity and manually setting all properties in one to the other, no difference.
I'm not particularly familiar with doctrine internals, whats going wrong here?
Try adding a __clone method to the entity class to null out the identifier so Doctrine recognizes it as a new entity:
public function __clone()
{
if ($this->id) {
$this->id = null;
}
}
Note the method is modeled after Doctrine's docs for safely implementing __clone
Related
I want to clone objects and save a duplicate in the database, but the problem is that the ID needs to be reset to null so that it can get the automatic sequence number in the database.
I am making use of Doctrine and Symfony2. I understand that I need to modify the __clone method of my entity, but I don't know how what to insert in it.
I have a basic clone Action:
public function cloneAction()
{
$object = $this->admin->getSubject();
if (!$object) {
throw new NotFoundHttpException(sprintf('unable to find the object with id : %s', $id));
}
// Be careful, you may need to overload the __clone method of your object
// to set its id to null !
$clonedObject = clone $object;
$this->admin->create($clonedObject);
$this->addFlash('sonata_flash_success', 'Cloned successfully');
return new RedirectResponse($this->admin->generateUrl('list', $this->admin->getFilterParameters()));
}
The documentation of Sonata says I have to do that, but I've never worked with __clone before, and I'm relative new with PHP. Any help will be nice please.
There was no problem. It was not necessary to overload the __clone method whatsoever. All I had to do was fixing my entity mappings and it worked.
I'm creating symfony2 application with doctrine2 and I would like to ask for advice regarding common/good practice for DTO-Entity, Entity-DTO conversion. I've found some information for all languages and frameworks, but none for SF2.
I would like to isolate Entities, so they are used only in Services and DAO's (Managers, Repositories in SF2 terminology). Controllers won't ever see DAO's or Entities and will interact with business logic only via Services. All communication between Services and Controllers should be done via primitive types, scalars, DTO's.
Example :
Controller
class RegistrationController extends Controller
{
public function registerAction($name)
{
$userDTO = new UserDTO();
$form = $this->createForm(new UserType(), $userDTO);
$form->handleRequest($request);
if ($form->isValid()) {
$userService = $this->get('userService');
$userService->createUser($userDTO);
return $this->redirect($this->generateUrl('success'));
}
--//--
}
}
Service
class UserServiceImpl implements UserService
{
private $userDao;
public function __construct(UserDao $userDao)
{
$this->userDao = $userDao;
}
public function createUser(UserDTO $user)
{
$user = new User(); #doctrine entity
$user->setFirstName($userDTO->getFirstName());
$user->setLastName($userDTO->getLastName());
$this->userDao->persist($user);
$this->userDao->flush();
--//--
}
}
Problem quickly appears with rising amount of properties in User object. In my application User has 13 fields. Are there any SF2 tools (classes) to simplify this process ? Do you write your own convertors / transformers ? Could you please show example of how it should look like ? Maby PHP magic methods could help ? What about reflection ?
Thanks for advices and opinions.
Start by using public properties on your dto's. That eliminates a bunch of getter/setter methods which really should not do anything for dto's. You can always add some majic methods for special cases.
Next, rethink the design of your DoctrineUserEntity aka Domain object. Do you really need getter/setter for each attribute? If so then what's the point?
Instead try to group properties into value objects:
$userNameValueObject = new UserNameValueObject($userDto->firstName, $userDto->lastName);
$userEntity = new UserEntity($userDTO->username,$userDTO->password, $userNameValueObject);
// And maybe this for updates
$userEntity->updateName($userNameValueObject);
But again, make sure you are actually getting some value for your work. A bunch of one to one mappings might make sense on other platforms where domain objects can stay alive between request. In php, everything starts from ground zero.
One option I've recently found is https://github.com/jasonrobertfox/DTOx which is a generator for DTO's and tests. It does the annoying boiler plate generation work for you.
In Yii ActiveRecord, is it possible to set an attribute in a model constructor and still have the model's isNewRecord property remain true?
I have a model constructor to create a private attribute to hold a PHPPass Password hash instance. When I do this, it sets the isNewRecord property to false, even though the record is created new.
I tried a workaround with call to setIsNewRecord(true) in the constructor if the id attribute is greater than zero, but it appears attributes are not available in the constructor.
I had to remove the constructor, and make the statement in each method requiring phpass.
Constructor for User model:
public function __construct(){
$this->phpass=new PasswordHash(Yii::app()->params['phpass']['iteration_count_log2'], Yii::app()->params['phpass']['portable_hashes']);
}
User model init and isNewRecord condition in controller
public function actionEdit($id = null)
{
$myModel = new User();
echo $myModel->isNewRecord; //false due to constructor
}
When ovveriding contstructor make sure to call parent constructor. Always call parent constructor when ovveriding, unless you know what you are doing.
public function __construct($scenario = 'insert'){
parent::__construct($scenario);
$this->phpass=new PasswordHash(Yii::app()->params['phpass']['iteration_count_log2'], Yii::app()->params['phpass']['portable_hashes']);
}
Also phpass can be stored as static property, you don't need a new instance of phpass for each active record instance.
EDIT: Thanks #Kevin for link to question about ovverding constructors.
One more important note to add: Remember to pass parameters to constructor. In Yii model classes there is $scenario param with default value insert. When you ovveride constructor without passing this argument, there will be always insert scenario, and there will not be any error message about missing param, because it has default value. I Updated code with $scenario.
In general you should avoid to override the constructor in Yii classes. Most components provide a init() method instead which is the right place for code like in your example.
The difference is, that for application components the object will already have all configuration applied. That's not relevant in your case but still it's good practice to use init() instead of __construct().
I have the Array collection of objects like this
Class User
{
private $tasks;
}
How can i empty or clear the collection once user gets loaded from database.
When i query for user then Doctrine will lazy load the tasks in user object but i want to first clear those tasks
something like
$user->getTasks().empty()
First of all, I imagine your User entity's constructor looks something like this:
class User
{
public function __construct()
{
...
$this->tasks = new \Doctrine\Common\Collections\ArrayCollection();
...
}
}
If that's not correct so far, then stop reading, and correct me in the comments :)
Note that the ArrayCollection class was created by Doctrine. Symfony and most of its components are pretty good about documenting the classes. When you look up that class, you'll find:
https://www.doctrine-project.org/api/collections/latest/Doctrine/Common/Collections/ArrayCollection.html
(of course, make sure you're on the same version; otherwise, try to find the documentation for your version)
The documentation lists all the methods available to the ArrayCollection object. Among them: clear().
That said, adding a new method to the User class should work:
class User
{
public function clearTasks()
{
$this->getTasks()->clear();
}
}
Then, on the User object, just call:
$user->clearTasks();
(and don't forget to persist to the database!)
Let's say I have entity $e. Is there any generic way to store it as another row, which would have the same entity data but another primary key?
Why I need this: I'm implementing some sort of Temporal Database schema and instead of updating the row I just need to create another one.
Try cloning and add the following method to your entity
public function __clone() {
$this->id = null;
}
You may need to detach the entity before persisting it. I don't have my dev machine handy to test this right now.
$f = clone $e;
$em->detach($f);
$em->persist($f);
$em->flush();
Update
Just tried using a simple SQLite demo. You shouldn't need to do anything. The following worked for me without adding a __clone() method or doing anything else out of the ordinary
$new = clone $old;
$em->persist($new);
$em->flush();
Once flushed, the $new entity had a new ID and was saved as a new row in the DB.
I would still null the ID property via the __clone() method as it makes sense from a pure model view.
Update 2
Digging into the Doctrine code, this is because the generated proxy classes implement __clone() with this important line
unset($this->_entityPersister, $this->_identifier);
I just do:
/**
* __clone
*
* #return void
*/
public function __clone()
{
$this->id = null;
}
More details here https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/cookbook/implementing-wakeup-or-clone.html
Copying the data in a new Object of the same class and persisting it will do. Keep it simple!