I've got these two models (changed the names for this example because they aren't in english):
Task:
public function times() {
return $this->hasMany('TaskTime', 'id');
}
TaskTime:
public function task() {
return $this->belongsTo('Task', 'task_id');
}
Also, inside the model Task, I've got this method:
public function start() {
$now = Carbon\Carbon::now();
$time = new TaskTime;
$time->task()->associate($this);
$time->beginning = $now->toDateTimeString();
$time->save();
// testing
echo $this->times()->get()->toJson();
echo '<br/><br/>';
echo $this->toJson();
die();
}
When I call the start() method, it correctly saves a new row in the TaskTime's corresponding table, with the foreign key correctly set to the Task.
The line echo $this->tempos()->get()->toJson(); correctly prints the rows, including the new one.
The line echo $this->toJson(); doesn't print the new row! Only prints the old ones.
I've tried save() and push() in both $this and $time and it still doesn't print the updated data!
Any idea of what can be causing this? I've been trying to debug this thing since yesterday and I ran out of ideas...
The problem is, that Eloquent does not update relation on the already loaded models after attaching, saving, associating etc.
It creates the relation, ie. inserts/updates necessary tables (attach, save, saveMany) or sets the relation on the model without saving anything in db (associate).
So in your case $this has no idea that newly created $tempo has been associated to it.
Now,
`$this->tempos()->get()->toJson();`
runs a new query to fetch related tempos, this is why you get correct result, but
`$this->tempos;
must have been loaded before associating new one, so they won't be reloaded from the db, thus you get 'old' result.
What you need is this:
public function start() {
// do what you need with $time
$tempo->task()->associate($this);
$tempo->save();
$this->load('tempos'); // reloads relation from db
// or:
$this->tempos->add($tempo); // manually add newly created model to the collection
}
Mind thought, that latter solution will cause unexpected result if $this->tempos have not been already loaded.
Related
We are trying to detect the changes in Laravel related models at attribute level, as we have to keep audit trail of all the changes which are made via the application.
We can track the changes via isDirty method on the Eloquent model for single model that is not related to any other model, but there is no way that we can track the changes on the related eloquent models. isDirty doesn't work on related models attributes. Can some one please help us on this?
Update to original question:
Actually we are trying to track changes on the pivot table that has extra attributes as well defined on it. IsDirty method doesn't work on those extra attributes which are defined in the pivot table.
Thanks
As much I understand your question, It's can achieve through Model Event and some sort of extra code with current and relation model.
Laravel Model Events
If you dont want to use any additional stuff, you can just use the Laravel Model Events (that in fact Ardent is wrapping in the hooks). Look into the docs http://laravel.com/docs/5.1/eloquent#events
Eloquent models fire several events, allowing you to hook into various
points in the model's lifecycle using the following methods: creating,
created, updating, updated, saving, saved, deleting, deleted,
restoring, restored.
Whenever a new item is saved for the first time, the creating and
created events will fire. If an item is not new and the save method is
called, the updating / updated events will fire. In both cases, the
saving / saved events will fire.
If false is returned from the creating, updating, saving, or deleting
events, the action will be cancelled:
Finally, reffering to your question you can utilize the above approaches in numerous ways but most obviously you can combine it (or not) with the Eloquent Models' getDirty() api docs here method and getRelation() api docs here method
It will work for example with the saving event.
Model::saving(function($model){
foreach($model->getDirty() as $attribute => $value){
$original= $model->getOriginal($attribute);
echo "Changed";
}
$relations = $model->getRelations();
foreach($relations as $relation){
$relation_model = getRelation($relation);
foreach($relation_model->getDirty() as $attribute => $value){
$original= $relation_model->getOriginal($attribute);
echo "Relation Changed";
}
}
return true; //if false the model wont save!
});
Another Thought might help you. when you saving
save() will check if something in the model has changed. If it hasn't it won't run a db query.
Here's the relevant part of code in Illuminate\Database\Eloquent\Model#performUpdate:
protected function performUpdate(Builder $query, array $options = [])
{
$dirty = $this->getDirty();
if (count($dirty) > 0)
{
// runs update query
}
return true;
}
The getDirty() method simply compares the current attributes with a copy saved in original when the model is created. This is done in the syncOriginal() method:
public function __construct(array $attributes = array())
{
$this->bootIfNotBooted();
$this->syncOriginal();
$this->fill($attributes);
}
public function syncOriginal()
{
$this->original = $this->attributes;
return $this;
}
check model is dirty isDirty():
if($user->isDirty()){
// changes have been made
}
Or check certain attribute:
if($user->isDirty('price')){
// price has changed
}
I did not check this code but hopeful to use as your answer by thoughts, if you have any confusion to deal such requirement or something need to optimize or change please let me know.
So I'm encountering a weird bug that is happening, but only some of the time. I can't seem to nail down anything that's causing it to happen. Basically, I have a ActiveRecord which corresponds with a table in my DB. I assign values to a object and call save().
Lets say I have the following code:
$student = new Student();
$student->name = "Gregg";
$student->surname = "Smith";
$student->gender = "Male";
$student->save();
Then my Student active record model I have the following:
public function afterSave()
{
$this->assignStudent($this->id);
}
$this->id IS not the correct ID after saving. I can see the ID in the log being 1416253 while the ID in the DB for that record is 670336. How can something like this happen?
UPDATE
I've done some more investigation and it seems that $this->id is assigned by assigning the last insert id back after doing the insert. Except that it is not returning the ID for the save() insert, but for a insert that was done previously (The insert in question was into the log table). This means that the insert into the student table is, according to the system, not happening correctly (even though it is, because I can see the record in the DB).
I am not sure how a wrong id can appear. Does the wrong id match another record of the table?
Have you tried something like this to be sure to be up to date ?
public function afterSave()
{
$this->refresh();
$this->assignStudent($this->id);
}
Also, the documentation states "When overriding this method, make sure you call the parent implementation so that the event is triggered" (documentation afterSave). So I think the correct way of writing all this may be:
public function afterSave($insert, $changedAttributes)
{
parent::afterSave($insert, $changedAttributes);
$this->refresh();
$this->assignStudent($this->id);
}
http://laravel.com/docs/4.2/eloquent#dynamic-properties
class Phone extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
}
$phone = Phone::find(1);
Now, if I then do something like this:
echo $phone->user->email;
echo $phone->user->name;
echo $phone->user->nickname;
Will Eloquent make a database call for every time I use the ->user dynamic property? Or is this smart enough to cache the user on the first call?
In your example, the user attribute on the $phone object will be lazy loaded, but it will only be loaded once.
Keep in mind, as well, that once the object is loaded, it does not reflect any changes to the underlying table unless you manually reload the relationship using the load method.
The following code illustrates the example:
$phone = Phone::find(1);
// first use of user attribute triggers lazy load
echo $phone->user->email;
// get that user outta here.
User::destroy($phone->user->id);
// echoes the name just fine, even though the record doesn't exist anymore
echo $phone->user->name;
// manually reload the relationship
$phone->load('user');
// now will show null, since the user was deleted and the relationship was reloaded
var_export($phone->user);
I'm trying to update a model, I load the model, take all the data from the POST and then save it, easy... But my record was never updating so went to the log and discovered that the update query is adding a weird condition. FYI, MD_ID is my primary key.
So, I load the model, the next line is the SQL produced by Yii:
$model = Ositems::model()->findByPk($id);
SELECT * FROM "MTODETALLADO_INV" "t" WHERE "t"."MD_ID"=249217
If echo the json_encode of the loaded model I get that dictionary in my browser:
echo json_encode($model->getAttributes());
{""MD_BODEGA":"01","MD_PRODUCTO":"0031253","MD_CANTIDAD":"1","MD_PRECIOTOTAL":"1466",,"MD_PORCENTAJEDESCUENTO":"0","MD_IDCABECERA":"97403","MD_ID":"249217","MD_OBSERVACION":null}
At this point everything looks right, now I take the values from post:
$model->attributes = $_POST;
And here if echo the values of the model I get the new values right, now here is the problem: I save the model and this is the SQL Yii runs (I replaced the :yp_ values to make it more readable)
$model->save();
UPDATE "MTODETALLADO_INV" SET
MD_BODEGA"='01'
MD_PRODUCTO"='0020514
MD_CANTIDAD"='10'
MD_PORCENTAJEDESCUENTO"='0
MD_IDCABECERA"=97403
MD_ID"=249218
MD_PRECIOTOTAL"='36210'
MD_OBSERVACION"=''
WHERE "MTODETALLADO_INV"."MD_ID"=1
And there is the problem! WHERE "MTODETALLADO_INV"."MD_ID"=1, Why would it make it 1 if all this time my model id has been 249218 ?
A few considerations:
My model only takes some columns that I need from the actual table, Yii sets the other columns as null and I omitted them in the previous code.
The table is in a foreign db, I use have a custom ActiveRecord which manages the CDbConnection to a database according to the user. (It's a webservice app)
I followed what the function save() did and could finally find the problem was when it tried to get the primary key. I had this method in my model:
public function primaryKey()
{
return array('MS_ID');
}
}
But it had to be:
public function primaryKey()
{
return 'MS_ID';
}
}
Somehow that was causing the problem.
I have discovered Kohana just 2 days ago and, after facing a few problems and managing to deal with them, I ran into a quite strange one.
When I try to update an entry in DB, instead of updating the entry ORM just inserts a new row.
Everything is similar to this example http://kohanaframework.org/3.0/guide/orm/examples/simple
My code (controller file):
class Controller_Admin extends Controller {
...
public function action_test($id)
{
$inquiry = ORM::factory('inquiry')->where('Id', '=', $id)->find(); // $inquiry = Model_Inquiry($id) doesn't work, too
$inquiry ->Name = 'Someone';
$inquiry ->save();
}
...
}
Model file:
class Model_Inquiry extends ORM
{
protected $_table_name = 'mytable';
}
Do you have any ideas why it's inserting a new entry instead of updating the old one? I read that it's because the ID is not correctly set, but I tried using a fixed value (->where('Id', '=', 5)) and it didn't work (it still inserted a new row).
Thanks in advance!
Your record isn't loaded in the first place. To check if it's loaded, do:
if ($inquiry->loaded()){...
Also, you can check what query has been run by doing:
echo $inquiry->last_query();
So you can manually check what exactly is being returned to ORM.
The main problem here is that you're using save() instead of being more strict and using create() / update(). If you used update(), ORM would throw an exception complaining about the record not being loaded.
Save is basically a proxy to these methods, relying on the loaded state.
(I assume that you're using Kohana 3.1 since 3.0 ORM doesn't have separate update / create methods at all)