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);
}
Related
I'm trying to use a function to soft delete 2 rows in a database, one is from a table I made called persona and the other one is from the users table that laravel makes with Auth, My users table have a foreign key associated to the persona table called idPersona. What I'm trying to do is to soft delete the row from the persona table that matches the id parameter and after that soft delete the row in users where the attribute idPersona matches the id parameter that the function recieved.
I'm going to post the Controller function code
Controller function code
public function deleteMedico($id){
$medico = Persona::findOrFail($id);
$medico->estado =False;
$personaID = $medico->id;
$user= new User();
$user->where('idPersona',$personaID);
var_dump($user);
}
I'm going to try to explain what I'm trying to do with this controller, I use the findOrFail function to find the row I'm trying to self delete from the persona table, after I found it I set estado to false in this way soft deleting the row. After that I try to get an User instance, look for the users table where idPersona matches the id of the row that was soft deleted.
The code for self deleting it would be almost the same than with the persona table but the problem is I can't find the row in the users table, gonna post the var_dump I get of $user.
Also gonna post how my database looks in case it is useful
I have no idea what I need to do to get the row of the users table where the idPersona attribute matches the id attribute from the personas table. I'm new in Laravel so sorry if this question is repetitive or is nothing to hard. p
Your syntax is wrong.
// $user = new User();
// $user->where('idPersona',$personaID);
$user = User::where('idPersona', $personaID)->firstOrFail();
This should fix the syntax but there are simpler ways to do this in your code.
Route model binding
Route::get('deleteMedico/{persona}', ...);
Eloquent Relationships
// Persona.php
public function user()
{
return $this->hasOne(User::class, 'idPersona');
}
public function deleteMedico(Persona $persona)
{
$persona->fill(['estado' => false])->save();
$persona->user->fill(['estado' => false])->save();
}
You forgot the get method.
User::where('idPersona',$personaID)->get();
I've a simple post form. Records are saved to database with updateOrCreate function.
I've following records to be stored,
user_id
product_id
review
rating
After submitting form, user_id and product_id are used to check if record exist or not. If exist, record will be updated otherwise new record created with the function.
public function updateOrCreate($input)
{
return $this->model->updateOrCreate(['product_id'=> $input['product_id'],'user_id'=>$input['user_id']],['rating'=>$input['rating'],'review'=>$input['review']]);
}
Is there any built in method to find new record created or existing record updated ?
Any kind of suggestion is appreciated.
There is a public property on Eloquent models called $wasRecentlyCreated. It is set when an insert is performed. And it will only be true after an insert for the same request; not for consecutive requests. If an update has been performed, it will be false.
Be aware that the property is also only true for the exact same model instance. If you reload the model with Product::find($id), it will be false. What happens if you call refresh() on a model, is unfortunately unclear to me.
You need to determine if the model or given attribute(s) have been modified. And wasChanged() method will help to identify if the model is updated or not.
$model = $this->model->updateOrCreate(['product_id'=> $input['product_id'],'user_id'=>$input['user_id']],['rating'=>$input['rating'],'review'=>$input['review']]);
// It will return true if the record have been modified
$wasChanged = $model->wasChanged();
if ( $wasChanged ) {
// updated
}
else {
// created
}
Hope this help you.
I'm asking this question in order to find the best practice to do it.
DB::table('owners')
->where('property_id',$id)
->update(array('person_id'=>$owner));
The problem is that in the table owners might not have a row to update. In that occasion i need to make an INSERT INTO instead of UPDATE.
My problem is that i have to run 2 queries each time, one for checking if the row already exists, and one more to update or insert into. Is it right to run 2 queries each time? Is there a better way to achieve that? I need to keep the queering processes fast for the user.
UPDATE: The table owners is a middle table of a many to many relationship. Unfortunately i cannot use ON DUPLICATE KEY.
well you could try to use firstOrCreate method of Laravel to check if user exists. After that retrieve the user object and pass it to an update function else if the user is not found firstOrCreate method will take care of you as it will create a new user with the data you will provide and will auto increment last user + 1 id.
There is also the option to use firstOrNew which will check if an instance exists based on the array values you passed and if no match is found it will auto create a new instance of the model you are handling for further manipulation.
Here is example with firstOrNew
Example Controller file.
public function getFirstUserOrNew($email)
{
$user = User::firstOrNew(['email' => $email]);
if($user)
{
$this->UpdateUser($user);
}
else
{
$this->CreateUser($user);
}
}
public function UpdateUser(User $user)
{
//Do update stuff
}
public function CreateUser(User $user)
{
//Do create stuff
}
P.S - I'm from Greece, if you want to discuss anything in native language send me a PM :)
EDIT:
Thanks to #Pyton contribution It seems you can also use an updateOrCreate method as it is explained here.
If you want to Update or Insert row You can use updateOrCreate
$owner = Owner::updateOrCreate(['property_id' => $id], ['person_id'=>$owner]);
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'm able to save data to the database and it creates a uuid in the primary key column(id). When I call $model->save(); and access $model->id afterwards, it returns:
{"expression":"UUID()","params":[]}
It should return the actual value in the database...How can I get the actual value that mysql stores?
I have a beforeSave function that adds in the uuid:
public function beforeSave() {
if ($this->isNewRecord)
$this->id = new CDbExpression('UUID()');
return parent::beforeSave();
}
I have also tried creating a trigger that executes the mysql uuid function before update on the id...The $model->id then returns null.
The problem is that you are telling Yii to run a MySQL function to insert into the DB. And it does so just fine, but Yii does not re-read the record afterwards to get the value. To re-read it would be inefficient because what you are doing is not very used and a read from the DB is not necessary usualy. You can go around this in 2 ways:
1) to read the value yourself from the db. You can do a $model->save(); $model->refresh(); right after and you should have the fresh data in the $model
2) you can make your function do a
public function beforeSave() {
if ($this->isNewRecord)
$this->id = Yii::app()->db->createCommand('select UUID()')->queryScalar();
return parent::beforeSave();
}
I have not tested this but you get the idea.
This will go to the DB, run UUID() and return to yii the result. By assigning to $this->id a string instead of a mysql command you will be able to use right away the value without doing a refresh. This will still use MySQL to get the UUID, so it will still put some stress on the server.
Usually I do something like
public function beforeSave() {
if ($this->isNewRecord)
$this->hash = hash('ripemd160',microtime());
return parent::beforeSave();
}
So I do not use the MySQL server at all, but because you use your column as the ID you might want UUID() to generate a truly unique ID.