If I create a new Doctrine object, with lots of relations, should I save() these relations before assigning them to newly created object?
E.g.
$main = new Main();
$child = new Child();
$main->child_rel = $child; // do I need to save the child obj explicitly?
$main->save();
I assumed that parent will automatically call cascading saves, but this doesn't seem to be the case for a newly instantiated parent object.
How does it really work?
Doctrine take care of everything and save related records if needed.
By the way, you don't need to instanciate an related object. You can use this syntax :
$user->Email->address = 'me#server.com';
$user->save();
In the case of one-to-many and many-to-many relationships :
$user->Phonenumbers[]->phonenumber = '123 123';
$user->Phonenumbers[]->phonenumber = '456 123';
$user->Phonenumbers[]->phonenumber = '123 777';
$user->save();
More infos on the doctrine documentation.
Related
I try to clone all records in my data entity that have the item value cf7c1ae00f
$dataEntity= new Data();
$cloneArray = $this->em->getRepository(Data::class)->findBy(['item' => 'cf7c1ae00f']);
foreach ($cloneArray as $cloneItem) {
$fieldClone = clone $cloneItem;
$dataEntity->setItem($fieldClone);
$this->em->persist($dataEntity);
}
$this->em->flush();
In my database there are 5 records. So I expect that another 5 records are added. But only one record is added.
You are writing the same $dataEntity 5 times with different contents. You could construct that object in the loop to solve your problem but you could also persist $fieldClone directly instead and skip the $dataEntity variable completely.
However, entities have unique ids and that will lead to errors when you try to persist a cloned entity. You would have to empty the id and other attributes that have to be unique in the collection / database.
You can easily set some initial values on a new object when the clone keyword is used, using the __clone() method of the class that the object belongs to.
So if you only need to empty the id, you would add a clone method to the Data class and change the loop to:
Data class:
public function __clone() {
$this->id = null;
}
Cloning code:
$cloneArray = $this->em->getRepository(Data::class)->findBy(['item' => 'cf7c1ae00f']);
foreach ($cloneArray as $cloneItem) {
# Clone the object and automatically run the `__clone()` method of the class
$fieldClone = clone $cloneItem;
$this->em->persist($fieldClone);
}
$this->em->flush();
User hasmany profiles
Profile belongs to user.
Following works:
$u = User::firstOrNew(['email' => $s['email']]);
$u->name = $s['name'];
$u->avatar = $s['avatar'];
$u->save();
$p = new UserProfile;
$p->provider = $s['provider'];
$p->provider_uid = $s['provider_uid'];
if ($u->profiles()->save($p)) {
}
But I don't really like it, is there a better more streamlined way? Why can't I save in 1 atomic insert?
You are trying to save data to 2 different table, that's why you can't do this using a single insert.
The way you do it - first save the parent object, then associate the child object and save that - is how it is usually done.
You could also have a look at the push() method of Eloquent's models, that works in a similar fashion as save() but also calls save() on related models. Using this method allows to replace this code:
$a = new A;
$a->save();
$b = new B;
$b->a()->associate($a);
$b->save();
with
$a = new A;
$b = new B;
$b->a()->associate($a);
$a->push();
I have a AR model that I am trying to duplicated but just need to manually change the foreign key.
$_POST['competition_id'] = 99;
$prizes = CompetitionPrizes::model()->findAll('competition_id =:competition_id',array(':competition_id'=> $_POST['competition_id']));
This query basically queries the prizes table and gets all the rows for a particular competition. With the prizes object I would like to basically re-insert/duplicate the same information except the competition id which I want to manually set.
I did something similar for an AR object that basically only has one row and that worked well, however in this instance as a competition can have more than one prize this same code won't.
// My existing code for duplication process
$obj = Competitions::model()->find('competition_id=:competition_id', array(':competition_id' => $post['competition_id']));
$clone = clone $obj;
$clone->isNewRecord = true;
unset($clone->competition_id); // i want to remove this so it is auto inserted instead via the db
$clone->save();
This works great - how would I modify this on a 'collection' of prizes and have this duplicated into the database while setting my own 'competition_id' value.
Note - i'm to new to Yii, so please let me know if I have made any obvious errors/bad practice
Cloning won't work. You need to assign the attributes to a new object:
$obj = Competitions::model()->find('competition_id=:competition_id', array(':competition_id' => $post['competition_id']));
$clone = new Competitions;
$clone->attributes = $obj->attributes;
$clone->save();
If a more generic way of duplicating a Model / ActiveRecord in Yii2 Framework is required, you might use this solution:
$copy = clone $model;
$copy->isNewRecord = true;
foreach ($model->getPrimaryKey(true) as $field => $value) {
unset($copy->{$field});
}
$copy->save();
GitHub issue discussion about duplicate models: https://github.com/yiisoft/yii2/issues/7544#issuecomment-77158479
The answer for my problem although Michiel above helped me out - alternatively if you wouldn't mind adding another answer i'll give you the accepted answer.
foreach($models as $model)
{
$clone = new Competitions;
$clone->attributes = $model->attributes;
$clone->competition_id = '123' // custom var i am setting manually.
$clone->save();
}
How about (yii2 syntax):
$model=Competitions::findOne([':competition_id' => $post['competition_id']]);
$model->id = null;
$model->isNewRecord = true;
$model->save();
I have a Post entity in my Blog bundle. Posts can have many comments. When I create a new comment entity to attach to a post I have to set a bunch of properties such as,
$comment->setTimestamp( new \DateTime() );
$comment->setUserId( $this->getUser()->getId() );
$comment->setHost( $this->getClientIP() );
default timezone is easy in the constructor of the entity. How do I automatically set the userid and clientip when constructing the entity? getClientIP is a function in the controller at the moment. This should be service. Can I have a factory that creates comments for me?
Seems to me that your best bet would be a class CommentFactory extends EntityFactory.
The factory would be responsible for creating your Entities for you, you pass the required entities (Such as the user entity) and it would return new objects for you:
$commentFactory = new CommentFactory($user, $client, $whatever);
$comment = $commentFactory->getNewComment();
You can call any entity method from an entity construct, and you can pass anything from controller to a new instance of the comment.
For instance, get timestamp, userid and host in controller action, and pass those as parameters to a construct of Comment entity.
Make calls to setter methods in a construct.
You can create helper function in controller for that
protected function getNewComment() {
$comment = new Comment();
$comment->setTimestamp( new \DateTime() );
$comment->setUserId( $this->getUser()->getId() );
$comment->setHost( $this->getClientIP() );
return $comment;
}
And then
$comment = $this->getNewComment();
I am using Paris with Idiorm and I am having problems finding in the documentation a clear instruction on how to find and update a table.
I don't want to insert a sql query into the script. Is there any other way?
Paris is an Active Record implementation based on Idiorm.
Idiorm is an object-relational mapper and fluent query builder.
I am interested in doing something like count = count + 1 all in one go
I found this on their github site:
Updating records
To update the database, change one or more of the properties of the object, then call the save method to commit the changes to the database. Again, you can change the values of the object's properties either by using the set method or by setting the value of the property directly:
$person = ORM::for_table('person')->find_one(5);
// The following two forms are equivalent
$person->set('name', 'Bob Smith');
$person->age = 20;
// Syncronise the object with the database
$person->save();
Creating new records
To add a new record, you need to first create an "empty" object instance. You then set values on the object as normal, and save it.
$person = ORM::for_table('person')->create();
$person->name = 'Joe Bloggs';
$person->age = 40;
$person->save();
After the object has been saved, you can call its id() method to find the autogenerated primary key value that the database assigned to it.
Checking whether a property has been modified
To check whether a property has been changed since the object was created (or last saved), call the is_dirty method:
$name_has_changed = $person->is_dirty('name'); // Returns true or false
According to the documentation on the github page, in idiorm you can update a record by doing the following:
$person = ORM::for_table('person')->find_one(5);
// The following two forms are equivalent
$person->set('name', 'Bob Smith');
$person->age = 20;
// Syncronise the object with the database
$person->save();
Or to do it in 'paris' you do:
$user = Model::factory('User')->find_one($id);
$user->name = 'Paris';
$user->save();