Eloquent updateOrCreate not updating - php

I am having trouble using updateOrCreate.
The below code is SUCCESSFULLY creating a new row; however when it comes to updating an existing row it doesn't work at all!
comm_helpful::updateOrCreate(
['ID' => 1],
['time' => 3000]
);
This works exactly as expected:
comm_helpful::where('ID', 1)->update(['time' => 3000]);
I've stripped the examples above back to bare minimums (which I did as part of debugging) and it's still not working!!

I have a similar problem.
My model had:
protected $fillable = ['user_x_cliente_id','internet_cliente_id'];
The status has 0.
$habilita = leilao_habs::updateOrCreate(
[
'user_x_cliente_id' => $user_x_cliente->id,
'internet_cliente_id' => $int_cli->id
],
['status' => 1]
);
dd($habilita);
After execute the updateOrCreate , the status still 0 and No get MassAssignmentException error.
The solution was change the model adding the status to protected $fillable:
protected $fillable = ['user_x_cliente_id','internet_cliente_id','status'];
Now the updateOrCreate works changing the status.

Ok, after almost completely pulling my hair out; I found the problem where Eloquent is setting:
protected $primaryKey = 'id';
which is used on the update method and returned by getKeyName().
protected function setKeysForSaveQuery(Builder $query)
{
$query->where($this->getKeyName(), '=', $this->getKeyForSaveQuery());
return $query;
}
I then realised my 'id' on the tale was uppercase and not lower case!
I guess that means my answer is that I need to ensure I am using lowercase 'id' for the primary key or overriding it on the Model
protected $primaryKey = 'ID';

Related

Can't get backpack to pass correct value when inserting/updating a many to many relationship

I have created a many to many relationship between two tables with a third pivot table.
The thing that makes the situation a little difficult is I am linking the Apps table based on name and not ID. It is because I update the App list from a third party and app name will always be consistent, where ID can possibly change if App is removed at some point, and then re-added, etc.
Apps
id
name // This is the name of the app, it will never change for a particular app and is short, all lowercase, no spaces, and unique
label // This is the user friendly name
Plans
id
name
etc
apps_plans pivot table
id
apps_name
plans_id
I've finally got everything working perfectly in Laravel itself, but I cannot figure out at all how to get this to work correctly in Backpack for my Admin portal. I've gotten it to the point where everything works perfect until I try to update or create a new plan. The Apps I select using the select2 type, it tries to insert them into the pivot table with an ID number and not with the name.
Randomizing some names, my mistake if things don't match perfectly. This aspect works fine from all tests I've done:
Plans Model:
{
use CrudTrait;
protected $table = 'plans';
protected $guarded = ['id'];
public function apps()
{
return $this->belongsToMany('App\Apps', 'apps_plans', 'plans_id', 'apps_name', 'id', 'name');
}
}
Apps Model:
class Apps extends Model
{
use CrudTrait;
protected $table = 'apps';
protected $guarded = ['id'];
protected $casts = [
'json' => 'array',
];
public function plans()
{
return $this->belongsToMany('App\Plan', 'apps_plans', 'apps_name', 'plans_id', 'name', 'id');
}
}
**Note I removed the fillable variable , I didn't want to expose all variables in my columns.
Backpack Plans CrudController:
public function setup()
{
CRUD::setModel(\App\Plan::class);
CRUD::setRoute(config('backpack.base.route_prefix') . '/plan');
CRUD::setEntityNameStrings('plan', 'plans');
$this->crud->addColumn([
'name' => 'apps',
'type' => 'relationship',
'label' => 'Apps',
'entity' => 'apps',
'attribute' => 'label',
'model' => \App\Apps::class,
]);
}
protected function setupCreateOperation()
{
CRUD::setValidation(PlanRequest::class);
CRUD::setFromDb(); // fields
$this->crud->addField('apps', [
'name' => 'apps',
'type' => 'select2_multiple',
'entity' => 'apps',
'attribute' => 'label',
'label' => 'Apps',
'pivot' => true,
]);
I removed quite a bit to keep my project details private, I hope it makes sense. I think all important details are still in. Anyone know if this is an issue with Backpack? Or did I miss an option somewhere, where you can set which column it uses for the relationship. It is clearly not taking it from the model because the models work just as intended on their own...
Thanks!
Edit: here is my migration I am using, it works flawlessly--even in phpmyadmin it gives me a drop down of items to select from
{
Schema::create('apps_plans', function (Blueprint $table) {
$table->id();
$table->string('apps_name');
$table->foreign('apps_name')->references('name')->on('apps');
$table->unsignedBigInteger('plans_id');
$table->foreign('plans_id')->references('id')->on('plans');
});
}
EDIT 2:
This is the error I am getting when trying to do a Create or Update:
{
"error": "There is a problem with your request",
"message": "SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`api`.`apps_plans`, CONSTRAINT `apps_plans_apps_name_foreign` FOREIGN KEY (`apps_name`) REFERENCES `apps` (`name`)) (SQL: insert into `apps_plans` (`apps_name`, `plans_id`) values (2, 4))"
}
Again I removed some details that were very specific to my project but I don't think I changed any of the logic in the error message. You can see everything looks great about the query except that at the very end, it is inserting the App ID instead of the App name as it should be.
I suspect that the current configuration will have the same result in Laravel directly. ie running something like $plan = Plan::find($somePlanId); $app = App::find($someAppId); $plan->apps()->attach($app); would result in the same error.
Since name is the key that matters for the apps table, consider dropping the autoincrementing id for that table and instead setting
In the migration for the apps table, do:
$table->string('name')->primary();
Then in your apps model, do:
protected $primaryKey = 'name';
public $incrementing = false;
protected $keyType = 'string';
Now, Laravel (and by proxy Backpack) should treat the relationship the way you expect.

After using save function for an eloquent model I got a wrong ID

I am using Laravel 5.8 with SQL Server, and I have an issue with saving a new user.
After I save using an eloquent model and I try to get an ID, the value of the ID is wrong. For example, in my DB, the ID value is 57, but in the model, the value is 27.
Dump: (for this row in DB Id value is 57)
#attributes: array:6
"Email" => "test#mail.com"
"Password" => "some_encrypted_value"
"Phone" => "+12312312312"
"UpdatedAt" => "2019-08-11 09:48:16.056"
"CreatedAt" => "2019-08-11 09:48:16.056"
"Id" => 27
Controller:
$user = new UserModel();
$user->Email = $body->email;
$user->Password = $this->encrypt($body->password);
$user->Phone = $body->phone;
$user->save();
dump($user); // **Here $user->Id value is 27, but in DB the Id value is 57**
Model:
class UserModel extends Model {
protected $table = 'User';
protected $primaryKey = 'Id';
protected $keyType = 'bigint';
}
Table:
CREATE TABLE [dbo].[User](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Username] [nvarchar](255) NULL,
[Password] [nvarchar](255) NOT NULL,
[Email] [nvarchar](255) NOT NULL,
[Phone] [nvarchar](255) NOT NULL,
[Activated] [bit] NULL,
[CreatedAt] [datetime] NULL,
[UpdatedAt] [datetime] NULL)
EDIT
I found the problem. On the User table I have a trigger that insert something in another table, and Laravel return last inserted Id in this connection not in this scope. Can i change somehow to return last inserted id in this table, not the one inserted by trigger?
You've stumbled upon a true Laravel flaw
Unfortunately, there's no neat way to solve this as Laravel always gets the last insert id in mssql with the ##IDENTITY statement rather than with SCOPE_IDENTITY(). However, this horrendous code, from here, should do the trick:
$user->save();
$tempQuery = $user->getConnection()->getPdo()->query("SELECT IDENT_CURRENT('User') AS id");
$user->Id = $tempQuery->fetchAll(\PDO::FETCH_OBJ)[0]->id;
I also could point you in another direction: Try changing the trigger itself. For example, what I could think now is to use something like INSERT IGNORE in your trigger so that the last inserted id gets persisted. If not enough, try googling solutions on that direction.
Hope it helps.
ORIGINAL ANSWER (not deleted because it's still valid):
Try removing the protected $keyType property from model.
class UserModel extends Model {
protected $table = 'User';
protected $primaryKey = 'Id';
}
I think you got the wrong idea about the protected $keyType property. It is not meant to receive the exact column type of your primary, but instead, it works along-side the public $incrementing property to help Eloquent guess what will its behavior about IDENTITY be.
In order words, if protected $keyType is not defined as int (default is int), Eloquent must probably be thinking it should be treated as a string and messing up somewhere.
I also had this problem. After a lot of research in my code, after insert record I insert another record into some table.
For this reason, eloquent return wrong ID.

How to authorize then update a Laravel model

I'm calling this controller to update a model:
public function update(Request $request, $id)
{
$question = Question::find($id);
$this->authorize('edit', $question); // uses Laravel's built-in Policy framework
$updateArray = [
'question' => $request->question,
'type_based_id' => $request->type_based_id,
];
//$question = Question::where('id', $id);
$question = $question->update($updateArray);
// Add the id into the array for return to the client
$updateArray["id"] = $id;
return ['message' => 'Question Updated', 'data' => $updateArray];
}
The code above throws a MassAssignmentException on the call to $question->update(). If I uncomment $question = Question::where('id', $id); it works.
I did some logging, and it seems that find() returns an instance of my model (App\Question) and where() returns a builder (Illuminate\Database\Eloquent\Builder)
How can I satisfy both authorize() and update() without making two separate database requests?
Thank you!
The reason it works using the Query Builder is because it by-passes the mass assignment checks of the model. You are running your own query and not using the Model's update method.
Question::where()->update is calling update on the Query Builder, not the Model.
There is no reason to use the query builder when you already have the model instance you are updating, but this isn't actually running any additional SQL queries.
MassAssignmentException usually means one of the attributes you're passing is guarded in the model. To unguard attributes, either remove them from the $guarded property or add them to the $fillable property of the model. Do NOT use both $guarded and $fillable, you must use one or the other. Read the full documentation here:
https://laravel.com/docs/5.5/eloquent#mass-assignment
MassAssigntmentException is due to the fields you are updating not being fillable and therefor being guarded from assignment, to achieve this you need to set these on the Question class.
public class Question
{
protected $fillable = [
'question',
'type_based_id',
];
}

Laravel - Array to string conversion error on Model::create or Model->save()

I'm performing a simple insert which I've done many times without any issues and for some odd reason it's not working and I get this error message:
error: {type: "ErrorException", message: "Array to string conversion",…}
file: "C:\wamp\www\studentreg2\vendor\laravel\framework\src\Illuminate\Database\Grammar.php"
line: 33
message: "Array to string conversion"
type: "ErrorException"
Here's my code:
$advisorCheck = AdvisorCheck::create([
'status' => Input::get('status'),
'application_id' => Input::get('id'),
'user_id' => Auth::id()
]);
The migration for the advisor_check table which AdvisorCheck model uses seems fine, all foreign keys are unsigned and show the relations correctly in phpmyadmin, all values from the Input::get are strings, the model has the correct fields set as fillable (status, application_id, user_id).
I've even tried doing this in php artisan tinker like this:
AdvisorCheck::create([ 'status' => 'returned', 'application_id' => '3', 'user_id' => '4']);
and I get this response:
Array to string conversion
I've also tried this method and get the same error:
$advisorCheck = new AdvisorCheck;
$advisorCheck->status = Input::get('status');
$advisorCheck->application_id = Input::get('id');
$advisorCheck->user_id = Auth::id();
$advisorCheck->save();
Model code:
<?php
class AdvisorCheck extends \Eloquent {
protected $fillable = ['status', 'application_id', 'user_id'];
protected $table = ['advisor_check'];
}
If you need to see more code please ask.
Many thanks to anyone who can help!
As you can see in the example that's shown in the Laravel docs the table property is a string and not an array
protected $table = 'advisor_check';
Which makes total sense if you think about it, since Eloquent models don't support multiple tables natively.
$table should be a string not array
<?php
class AdvisorCheck extends \Eloquent {
protected $fillable = ['status', 'application_id', 'user_id'];
protected $table = 'advisor_check';
}
if we give table name in laravel model like array:
protected $table = ['thoughts'];
then it will generate error.
so you should give table name as string like :
protected $table = 'thoughts';

Kohana 3.2. table name not found

I'm using kohana 3.2. and I'm having some trouble getting this new model to work..
There's the model: Model_User_Unavailability which works fine and there's the model Model_User_Unavailability_Status which doesn't.
The problem isn't finding the model, it's using it. I can make a status using: ORM::factory('user_unavailability_status'); and it works just fine, but when I want to add the status to the unavailability class it won't work and I'm getting the following exception:
Incorrect table name '' [ INSERT INTO `` (`user_unavailability_id`, `status_id`) VALUES ('670', NULL) ]
The classes look like this:
class Model_User_Unavailability extends ORM
{
protected $_belongs_to = array(
'user' => array(),
);
protected $_table_name = 'user_unavailability';
protected $_has_many = array(
'status' => array(
'model' => 'User_Unavailability_Status',
'foreign_key' => 'user_unavailability_id'
)
);
...etc
­
class Model_User_Unavailability_Status extends ORM
{
protected $_table_name = 'user_unavailability_status';
protected $_belongs_to = array(
'user_unavailability' => array(
'foreign_key' => 'user_unavailability_id',
'model' => 'user_unavailability'
)
);
etc...
Now the exception appears when I try the following:
$db = $user->unavailable->where('id', '=', $id)->find(); // Returns User_Unavailability object
//... do some other stuf...
$db->save();
if($db->loaded())
{
$status = ORM::factory('user_unavailability_status');
$status->status = 'requested';
$status->responder_id = 1;
$db->add('status',$status);
}
As you can see in the exception earlier it won't use the specified table name. However, when I remove the $_table_name variable from the status class I get the following error:
Table 'urenregistratie.user_unavailability_statuses' doesn't exist [ SHOW FULL COLUMNS FROM `user_unavailability_statuses` ]
As you can see it does find the model, but won't find the database table. So my question here is, what's going on here? I guess I'm missing something but I don't know what...
Same happens when i use the $this->_table_names_plural = false in the status model.
So... any suggestions / ideas?
Edit:
It seems to work when I change the $db->add('status',$status); line to:
$status->user_unavailability_id = $db->id;
$status->save();
Not pretty though and still wondering what's preventing me from using the add function.
add() is used in has_many "through" relationships.
In your case you have already written the correct way:
$status->user_unavailability_id = $db->id;
$status->save();

Categories