Laravel model ID null after save (ID is incrementing) - php

I have a save method where I do validation, create a model and save it. Later in the save method I try and access the ID of the model and it's NULL.
Schema:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->string('body');
//and other fields that aren't important right now
$table->timestamps();
});
}
Save method:
public function savePost(Request $request) {
$this->validate($request, [
//some validation happens here
]);
$post = new Post();
$title = $request->input('title');
$body = $request->input('body');
$post->title = $title;
$post->body = $body;
$post->save();
//I later try to access the $post->id here and it's still NULL.
var_dump($post->id);//NULL
}
In MySQL the ID is being set (just an auto incrementing unsigned integer) - it seems like it gets set after the savePost method is complete.
I thought that after $post->save() was complete, the model's ID would be set - but that seems to not be the case. How can I access the model's ID from the same method where I save() it? Or have I made a mistake somewhere?
Laravel Framework version is 5.3.26
Edit: just to clarify: the model (including the auto incrementing PK) is being stored in MySQL, I'm just not able to access the model's id in the same method that I save it.

Figured it out:
I ran into this issue when switching from a string PK to an auto incrementing unsigned int.
I still had public $incrementing = false; in the Post model, which explains this issue. Switching to $incrementing = true solved the problem.

For other people how are using Pivot/Junction table, hence, if your model is extending Pivot instead of Model then this is why,
As #noob mentioned, You've to enable public $incrementing = true; manually or just extend Model instead.

Related

Laravel Create Method does not return primary key values that were inserted by Observer

Let me preface this post with, I cannot change the method of inserting the primary key. This is being developed on a legacy system and I have no control over the method of retrieving the primary key, I just have to deal with it.
I have found that Laravel will not update the collection primary key when using the create method, with an observer that inserts the primary key value.
Here is my scenario (I have shrunk the models and files for space):
migration file:
Schema::create('forms_maps', function (Blueprint $table) {
$table->integer('id')->unsigned();
$table->string('name');
});
ModelObserver.php:
public function creating(Model $model)
{
$countername = strtolower(class_basename(get_class($model))).'s_id';
$model->id = tap(\App\Models\OCounter::where('countername',$countername)->first())->increment('counterval')->fresh()->counterval;
Log::debug("Creating ". strtolower(class_basename(get_class($model))) . ": " . $model);
}
DatabaseSeeder.php:
$accountApp = \App\Models\FormsMap::create(['name' => 'Account Application']);
Log::debug("Created formsmap: " . $accountApp);
The output log:
Creating formsmap: {"name":"Account Application","id":84}
Created formsmap: {"name":"Account Application","id":0}
As you can see from the log, when the record is created using the create method, inside of the observer, I am getting the proper id; however, that value is not being passed back to the collection in the DatabaseSeeder. Am I looking at this incorrectly? Should I be using something else to insert the values into the tables? I do not want to insert this value manually/inline because every model has to have this information injected.
Thanks!
GRRR!!!! I do this every time! The answer is:
The model needs to have the incrementing turned off.
class FormsMap extends Model
{
public $timestamps = false;
public $incrementing = false;
...
}
Heh!

How to disable Laravel eloquent Auto Increment?

I need an effective way to disable Laravel from auto incriminating the primary key of the table which I am going to insert data in.
Why? I don't check if there is any duplication between the DB and the inserted data so if there was any duplication I just handles it in a try-catch block.
The problem is if there was any failure Laravel counts it like I have inserted a row. So IDs column is not going to be in this order [1, 2, 3, etc], but in this [1, 4, 8, 20, etc].
I searched a lot about this issue and I have tried to use this line after the declaration of the class:
public $autoincrement = false;
Also
public $incrementing = false;
But they are not working.
I just want to use the AI of my DB. Not Laravel's one.
if you wish to use a non-incrementing or a non-numeric primary key you must set the public $incrementing property on your model to false.
eg :
class UserVerification extends Model
{
protected $primaryKey = 'your_key_name'; // or null
public $incrementing = false;
}
in case of migration :
$table->integer('id')->unsigned(); // to remove primary key
$table->primary('id'); //to add primary key
refer : https://laravel.com/docs/5.3/eloquent#eloquent-model-conventions
Try public $incrementing = false;
In your model:
public $incrementing = false;
In your migration:
//Remove the default $table->id();
//second param is what auto-increments, default is false so can be skipped
$table->unsignedBigInteger('id', false)->primary();
A quick comment on all the other outdated or wrong solutions you see here:
$primaryKey does not need to be overridden unless the primary column is different than 'id'
You do not need to use the ugly DB transaction just to remove auto-incrementing! Keep using the lovely eloquent models and just use this answer.
There are 2 solutions to your problem.
First one is, as you said, disable the increment column. To do that, just go to your migrations, and change
$table->increment('id)`
to
$table->integer('id')
It will remove the primary key, to set the primary key, just go to your Model file and add this:
protected $primaryKey = 'column_name';
Second solution is what I prefer. Whenever inserting, updating or removing a record and even sometimes reading a record, use laravel's DB transaction. Here is an example:
DB::beginTranscation();
try {
$model = new Model;
$model->column_name = $value;
$model->save();
DB::commit()
return;
}
catch(exception $e) {
DB::rollback();
return;
}
This approach is better than remove the auto increment. But now it's upto you to choose.
You have to declare your new primary key in your table migration file:
$table->primary('new_key_column');
Of course you have to disable autoincrement in your model as you did.
You can do something like below
Table: author
Columns: id, name, active
class Author extends Model
{
/**
* Configure the Model variables
*
*/
protected $table = 'author';
protected $primaryKey = 'id';
protected $fillable = ['name', 'active']; // eloquent Will use only these columns as you are mentioning them as fillable.
public static function saveAuthor($data) {
$author = Author::firstOrNew(['name' => $data['name']]);
$author->name = $data['name'];
$author->active = 1;
$author->save();
}
}
Here in fillable you define the columns that you want to change or update through model. Rest fields behaviour will take according to mysql definition.
I hope this will help you.
*/ please test this one
public function up()
{
Schema::create('tablename', function (Blueprint $table) {
// $table->increments('id');
$table->integer('id')->unsigned()->nullable();
$table->string('name');
$table->string('info');
$table->timestamps();
});
}
// note:
add below line in model
public $incrementing = false;
This is an example for table created its name site_rules and this is the migration file which i add the following line to make id primary and auto incremented
//add this line to make id auto incremented from a specified value
DB::statement("ALTER TABLE site_rules AUTO_INCREMENT = 1;");
and this is the migration file code :
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class SiteRules extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('site_rules', function (Blueprint $table){
$table->increments('id');
$table->string('name_ar');
$table->string('name_en');
$table->timestamps();
});
//add this line to make id auto increment from a specified value
DB::statement("ALTER TABLE site_rules AUTO_INCREMENT = 1;");
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('site_rules');
}
}
Try it
Sorry for taking your time guys,
The issue is if we tried to save any record in MYSQL whether it returned success or failure for any reason (like for duplication in my case),
MYSQL counts that the auto increment number was booked and any other coming record is not going to take it because of there may be more than an insertion process on the DB in the same time and waiting to know if the auto incrementally number is booked or not will cost MYSQL its speed.
So it is not a Laravel issue, its a MYSQL one.
I hope that it may help others.
Thank you all...
You can use
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->integer('matricule')->unsigned();;
$table->primary(['matricule']);
$table->string('nometprenom');
$table->string('age');
$table->string('adresse');
$table->string('telephone');
$table->string('login');
$table->string('password');
$table->string('type');
$table->engine = 'InnoDB';
});
}

Dealing with Eloquent relationships in Laravel 5.3

I have the following tables created using Schemas:
Schema::create('typen', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->unique();
});
Schema::create('auktionen', function (Blueprint $table) {
$table->increments('id');
$table->integer('typ')->unsigned();
$table->foreign('typ')->references('id')->on('typen');
});
The table typen is only created once at contains fixed values:
Typen
id| name
1 | Typ 1
2 | Typ 2
...
Those values are constant, no more will be added during the application lifetime. So every Auktion I create should be associated with one Typ.
I created the model for Auktion and thought of it as a one-to-one relationship (is this correct?).
I would like to create an Auktion with a given Typ using Eloquent queries like this:
$thetyp = App\Typ::where("id", "=", 1)->first();
$auktion->typ()->associate($thetyp);
and fetch the name of the Typ of my Auktion:
$auktion->typ->name;
Currently my model looks like this for Auktion:
public function typ()
{
return $this->hasOne('App\Typ', 'typ');
}
which is not working. I already tried setting different relationships but I just end in different error codes ranging from undefined methods (associate() to an error where an SQL statement failed trying to update my typen table (when using save() - which I really do not want to).
Can someone clarify this problem for me and explain which relationship I have to use?
EDIT1: As mentioned in a comment I already tried using belongsTo in Auktion model
public function typ()
{
return $this->belongsTo('App\Typ', 'typ');
}
which results in Undefined property: App\Auktion::$typ when calling
$thetyp = App\Typ::where("id", "=", 1)->first();
$auktion->typ()->save($thetyp);
and when calling $auktion->typ->name; in
Trying to get property of non-object
EDIT2:
I just figured that
echo $auktion->typ()->first()->name;
is indeed working. But referring to this answer this should be the same as
echo $auktion->typ->name;
What exactly am i doing wrong?
EDIT3:
I tried using suggested code:
$thetyp = App\Typ::find($typ);
$auktion->typ->save($thetyp);
After I navigated to the view ehere I run the code I got this:
I got this the second time today, somwhow out of nowhere
Here is some code enhancement:
$thetyp = App\Typ::where("id", "=", 1)->first();
$auktion->typ()->save($thetyp);
To:
//Will return null if Model is not found
$thetyp = App\Typ::find(1);
//You actually have to retrieve the relationship, because calling typ()
//will only retrieve the Relationship query
$auktion->typ()->get()->save($thetyp);
The problem is that the relationship is defined backwards. You need to make Auction belongTo Type, and change Type to hasMany Auctions. The statement would read:
"A Type has many Auctions. An Auction has one Type".
Here are the classes (in English, sorry, my German is bad :( so I just did it in English) with the migrations:
-Auction class:
class Auction extends Model
{
protected $table = 'auction';
public function type()
{
return $this->belongsTo('App\Type');
}
}
-Auction migration:
Schema::create('auction', function(Blueprint $table){
$table->increments('id');
$table->integer('type_id')->references('id')->on('type')->nullable();
$table->string('title')->nullable();
$table->string('description')->nullable();
$table->timestamps();
});
-Type class:
class Type extends Model
{
protected $table = 'type';
public function auction()
{
return $this->hasMany('App\Auction');
}
}
-Type migration:
Schema::create('type', function(Blueprint $table){
$table->increments('id');
$table->string('name');
$table->timestamps();
});
First, you can create a Type object (or insert it with a query) so we can have a Type row that we can relate to an Auction object/entry, and do the following:
//Example of Type obj creation
$type = new Type();
$type->name = 'Type #1';
//Don't forget to save
$type->save();
//OR find/retrieve a Type obj
//Where $id is the id of the Type obj, else the obj will be null
$type = Type::find($id);
$auction = new Auction();
$auction->title = 'Title #1';
$auction->description = 'Test description';
$auction->type_id = $type->id;
//Don't forget to save
$auction->save();
Now later in your code, whenever you are using an Auction object and you want to retrieve the associated type (if any), you can use:
$type = $auction->type()->get();
Which will return the instance of Type, and you will be able to retrieve the property name like so:
$type->name
I hope this helps! Let me know if you have any more questions!
Your second edit makes a lot of sense. Your method name is typ but that's also the name of the column. So when you use $auktion->typ() it's actually loading the relationship. When you use $auktion->typ it's grabbing the value of that column.
You need to either continue working this way using the parenthesis to load the relation and no parenthesis to grab the value of the column, or you can change the name of the column to something better such as typ_id which is ultimately what Laravel expects it to be and should save you from more similar headaches down the road.
You can try this in your Auktion model
$thetyp = App\Typ::find(1);
$auktion->typ->save($thetyp);
Now fetch
$auktion->typ->name

Get relationships after insert

In my code, I insert a new row into the database:
$post = new Post;
$post->user_id = Auth::user()->id;
// more inserts
$post->save();
In my Post.php, I have:
protected $with = [
'user', 'answers', 'questions'
];
public function users()
{
return $this->belongsTo('App\User');
}
// etc
But when I return $post after I insert, there are no relationships (users, answers, questions) attached to it.
How can I get all of the default relationships to load after an insert?
The save() method persists the data to the database, but it doesn't do anything about refreshing the data on the Model or reloading relationships.
The easiest solution would be to refresh your object after calling save(). This will automatically eager load the relationships you've defined in your $with property on the model:
// ...
$post->save();
// refresh the post from the database
$post = $post->fresh();
Another option is to just manually reload the relationships yourself, using the load() method.
// ...
$post->save();
// reload the desired relationships
$post->load(['user', 'answers', 'questions']);
However, this duplicates the code that defines the relationships you'd like to be auto loaded (defined once in the Model, and then once in this code). You can mitigate that by creating a new function on your Model.
// in Post model
public function reloadRelations() {
$this->load($this->with);
}
// code usage
// ...
$post->save();
// call your new function to reload the relations
$post->reloadRelations();
However, the only real benefit of going this route over just calling the built in fresh() method is that this won't re-run the query to get the original Post data.
If you're handling 1000s of requests a second, maybe the one query might make a difference, but other than that, I wouldn't worry about it, and just use the fresh() method. But, the options are here for you to choose.
Instead of manually setting the attribute user_id, you should use the associate method from \Illuminate\Database\Eloquent\Relations\BelongsTo class.
$post->user()->associate(Auth::user());
// now you have the user inside your post.
dd($post->user);
May be, in model Post.php:
protected $primaryKey = 'id';
public function users()
{
return $this->hasOne('App\User', 'id', 'user_id');
}
before:
migration "posts"
Schema::create('articles', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
// .....
});
Hopefully this will solve your problem

Laravel Unknown Column 'updated_at'

I've just started with Laravel and I get the following error:
Unknown column 'updated_at' insert into gebruikers (naam, wachtwoord,
updated_at, created_at)
I know the error is from the timestamp column when you migrate a table but I'm not using the updated_at field. I used to use it when I followed the Laravel tutorial but now that I am making (or attempting to make) my own stuff. I get this error even though I don't use timestamps. I can't seem to find the place where it's being used. This is the code:
Controller
public function created()
{
if (!User::isValidRegister(Input::all())) {
return Redirect::back()->withInput()->withErrors(User::$errors);
}
// Register the new user or whatever.
$user = new User;
$user->naam = Input::get('naam');
$user->wachtwoord = Hash::make(Input::get('password'));
$user->save();
return Redirect::to('/users');
}
Route
Route::get('created', 'UserController#created');
Model
public static $rules_register = [
'naam' => 'unique:gebruikers,naam'
];
public static $errors;
protected $table = 'gebruikers';
public static function isValidRegister($data)
{
$validation = Validator::make($data, static::$rules_register);
if ($validation->passes()) {
return true;
}
static::$errors = $validation->messages();
return false;
}
I must be forgetting something... What am I doing wrong here?
In the model, write the below code;
public $timestamps = false;
This would work.
Explanation : By default laravel will expect created_at & updated_at column in your table.
By making it to false it will override the default setting.
Setting timestamps to false means you are going to lose both created_at and updated_at whereas you could set both of the keys in your model.
Case 1:
You have created_at column but not update_at you could simply set updated_at to false in your model
class ABC extends Model {
const UPDATED_AT = null;
Case 2:
You have both created_at and updated_at columns but with different column names
You could simply do:
class ABC extends Model {
const CREATED_AT = 'name_of_created_at_column';
const UPDATED_AT = 'name_of_updated_at_column';
Finally ignoring timestamps completely:
class ABC extends Model {
public $timestamps = false;
}
Link to laravel documentation https://laravel.com/docs/9.x/eloquent#timestamps
Nice answer by Alex and Sameer, but maybe just additional info on why is necessary to put
public $timestamps = false;
Timestamps are nicely explained on official Laravel page:
By default, Eloquent expects created_at and updated_at columns to exist on your >tables. If you do not wish to have these columns automatically managed by >Eloquent, set the $timestamps property on your model to false.
For those who are using laravel 5 or above must use public modifier other wise it will throw an exception
Access level to App\yourModelName::$timestamps must be
public (as in class Illuminate\Database\Eloquent\Model)
public $timestamps = false;
In case you still want the timestamps, but simply forgot to add them in the migration, adding the following to your migration file, will also work:
class AddUsersTable extends Migration
{
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->timestamps(); // <-- Add this to add created_at and updated_at
});
}
}
Don't forget to re-run your migration afterwards.
php artisan migrate:rollback
php artisan migrate
First Solution
If not necessary created_at and updated_at, please write the below code in the model.
public $timestamps = false;
Second Solution
If you need to use created_at and updated_at in the future, you can add columns.
Create migration file:
php artisan make:migration add_timestamps_fields_to_users_table
Edit migration file:
class AddTimestampsFieldsToUsersTable extends Migration
{
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->timestamps();
});
}
}
Run migration:
php artisan migrate

Categories