Add element to doctrine ArrayCollection by Id - php

I find very annoying to have to fetch an object by id from database every time I want to add it to a relationship. Is there a way to add an object to a a relationship by id instead of adding the whole object?
This is my actual code:
...
$person = $em->getRepository("Person")->findOneById($id);
$me->getPersons()->add($person);
...
I would like to have something like this:
...
$me->getPersons()->add($id);
...
Then I would be saving one trip to the database! Which I like better! Is it possible?

You don't have to do that actually. You can get reference object like so:
$person = $em->getReference("Person", $id);
$me->getPersons()->add($person);
Doctrine will not make a query for Person but will instead return an reference proxy object for person with that id. If you, however do:
$person = $em->getReference("Person", $id); // 0 queries
$person->getId(); // Still 0 queries
$person->getSomeField(); // query fired
Doctrine will trigger lazy load if you try to get some field that has to be fetched from database.
See docsEntityManager::getReference method

Related

Catch and save elements in Laravel, not by primaryKey

I need to get an element from the database, but I can not get it by the FIND method, since FIND only finds it by the primaryKey and what I need is not by my primaryKey. So I did like this:
$user = Pac::find($request->pac_id);
$element = query()->where('med_cart', $user->pac_id)->get();
$element->med_obs = $request->med_obs;
$element->save(); // error
Now I need to save this element, however, I can not use the SAVE method, as I believe it is fully connected with FIND and FINDORFAIL (if anyone knows, explain to me which methods I can use the SAVE method).
How can I save them the way I did? Or is there some other way to do it?
Because I need to get the element with a data other than the primaryKey and then save it, then I can not use FIND or FINDORFAIL, I think.
The function ->find() returns an Eloquent Model instance and you can then call ->save() on the model instance.
You're using ->get() which returns a Collection.
To update your query (that may target one or more entries) just perform the update statement directly from the QueryBuilder by replacing ->get() with ->update(['med_obs' => $request->med_obs]).
Be aware that when doing this you are now using Fluent queries, instead of eloquent. This means that any logic you may have defined in the boot function of your model is not evaluated.
If you are certain that you only have a single result you can append ->first() to your query, which will return a Model of the first result that matches your ->where clause. You can then call ->save() on it:
$user = Pac::find($request->pac_id);
$element = query()->where('med_cart', $user->pac_id)->first();
$element->med_obs = $request->med_obs;
$element->save();

Eloquent - Updating all models in a collection

I want to set a certain attribute in all the models of a collection.
in plain SQL:
UPDATE table SET att = 'foo' WHERE id in (1,2,3)
the code i have:
$models = MyModel::findMany([1,2,3]);
$models->update(['att'=>'foo']);
taken from here
but doesn't work. I'm getting
Call to undefined method Illuminate\Database\Eloquent\Collection::update()
the only way i have found it's building a query with the query builder but i'd rather avoid that.
You are returning a collection, not keeping the query open to update. Like your example is doing.
$models = MyModel::whereIn('id',[1,2,3]);
$models->update(['att'=>'foo']);
whereIn will query a column in your case id, the second parameter is an array of the ids you want to return, but will not execute the query. The findMany you were using was executing it thus returning a Collection of models.
If you need to get the model to use for something else you can do $collection = $models->get(); and it will return a collection of the models.
If you do not just simply write it on one line like so;
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
Another option which i do not recommend is using the following;
$models = MyModel::findMany([1,2,3]);
$models->each(function ($item){
$item->update(['att'=>'foo']);
});
This will loop over all the items in the collection and update them individually. But I recommend the whereIn method.
The best solution in one single query is still:
MyModel::whereIn('id',[1,2,3])->update(['att'=>'foo']);
If you already have a collection of models and you want to do a direct update you can use modelKeys() method. Consider that after making this update your $models collection remains outdated and you may need to refresh it:
MyModel::whereIn('id', $models->modelKeys())->update(['att'=>'foo']);
$models = MyModel::findMany($models->modelKeys());
The next example I will not recommend because for every item of your $models collection a new extra query is performed:
$models->each(function ($item) {
$item->update(['att'=>'foo']);
});
or simpler, from Laravel 5.4 you can do $models->each->update(['att'=>'foo']);
However, the last example (and only the last) is good when you want to trigger some model events like saving, saved, updating, updated. Other presented solutions are touching direct the database but models are not waked up.
Just use the following:
MyModel::query()->update([
"att" => "foo"
]);
Be mindful that batch updating models won't fire callback updating and updated events. If you need those to be fired, you have to execute each update separately, for example like so (assuming $models is a collection of models):
$models->each(fn($model) => $model->update(['att'=>'foo']) );

PHP Activerecord: updating array of objects

The following code will return an array of PHP Activerecord Objects:
$book = Book::find('all');
Assuming the program is aware of the order of books I can continue and update the attributes of the books and save them to the database as follows:
$book[0]->title = 'my first book';
$book[0]->author = 'Danny DeVito';
$book[4]->title = 'Nice Title';
in order to save the above I would have to invoke the ->save() method on each object
$book[0]->save();
$book[4]->save();
Is there a better way to do this? built-in PHP ActiveRecord function
that saves all members of a given array of objects, or based on an
association?
Assuming the original title of $book[4] above was already 'Nice
Title', would the ->save() method consider $book[4]changed and
continue with the database save?
Try using update all insted
$update = array();
$update['title'] = 'my first book';
$update['author'] = 'Danny DeVito' ;
$book[0]->update_all(array('set' =>$update));
$book[4]->update_all(array('set' =>array("title"=>"Nice Title"));
I think this should be cleaner
After much research I decided to post my conclusions/answers:
There is no such ActiveRecord library function that can update an
array of objects with unique values.
Assuming Activerecord would shoot one update request it would look like this:
UPDATE books
SET title = CASE id
WHEN 0 THEN 'my first book'
WHEN 4 THEN 'Nice Title'
END,
author = CASE id
WHEN 0 THEN 'Danny DeVito'
END
WHERE id IN (0,4)
The same question as "how would I update multiple rows with different values at once". This would go against the design of an Activerecord model, as an Object represents a row, and maps rows across tables. An obvious limitation for having such an easy model to work with.
Any assignment to an Object's attributes triggers a 'dirty' flag on
that attribute, and any subsequent call to update/save that
object will trigger a query even if the assigned attribute value is
the same as the database/model's previous value. Invoking the
save() method when no assignments were made does not trigger this
query.

Yii saving object data after find

For Yii framework, can we set attribute for save function using object results from find function like below?
$proposal = new Proposals;
$proposal_temp = Proposals_temp::model()->find('eid=?', array($users->eid));
$proposal->Attributes = $proposal_temp;
Would this actually save the Proposal_temp row into Proposals table?
No, you'll have to use the save() method. Attributes is just an array of columns in the table. You'll have to loop through the results of $proposal_temp and save each row. See Documentation

Update object instead of inserting with Doctrine using assignIdentifier()

I'm pretty new to Doctrine, but as I understand it, the assignIdentifier() method is supposed to tell Doctrine to update the relevant row into the database instead of inserting a new one.
I have an object that I'm building through a workflow, so the identifier has an id of null until I call $object->save(); which inserts it, and this does work.
If however I call $object->assignIdentifier($newobj->id); and then $object->save(); it does nothing - it does not insert a new row and does not update the old one.
If a certain condition is true, I want to pull a different record out of the DB and update that row instead of inserting the new one.
Am I understanding something wrong here?
Some code to illustrate:
if($this->object->payments > 0) {
$older_payment = Doctrine_Query::create()
->from('OldPaid p')
->where('p.dealid = ?', $this->object->transid)
->fetchOne()
;
$this->object->assignIdentifier($older_payment->id);
}
$this->object->save();
Like i got to know, save() will not update an existing record with autoincrement on ID.
I have the same problem using doctrine 1.2.
an idea i have use this one, the only workaroung i found:
$query = Doctrine_Query::create()->update('OldPaid');
$query->set($yourFieldname, '?', $yourValue);
$query->addwhere('p.dealid = ?', $this->object->transid);
$query->execute();
Thiw will function when a record is in the DN with the primaryKey dealid = $this->object->transid.
greeting m
Usually, if you retrieve a record, you can update it with the save() method. Doctrine recognizes this (since the PK doesn't change) and updates the record.
From the docs:
Updating objects is very easy, you
just call the Doctrine_Record::save()
method
Another way can be replace(), but I usually use just save() and does either the saving or the updating if the record already exists.
As far as I can read from the description of assignIdentifier() never used it myself) it will only work with retrieving an object by its ID, so updating something with this method will not work.

Categories