Saving model with not nullable relationship - php

Consider the following two relations:
User:
CREATE TABLE user (
id INT NOT NULL,
social_provider_id INT NOT NULL,
CONSTRAINT `user_social_provider_id_fk` FOREIGN KEY (`social_provider_id`)
REFERENCES `social_provider` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
)
Social provider:
CREATE TABLE social_provider (
id INT NOT NULL,
name VARCHAR NOT NULL )
Eloquent models:
class User extends Model{
protected $table = 'user';
public function socialProvider(){
return $this->hasOne('/SocialProvider');
}
}
Now I want to create a new User object and save it:
$user = new User();
$socialProvider = SocialProvider::find(1);
$user->socialProvider()->save($socialProvider);
$user->save();
But I get this exception on the line where I assign the SocialProvder object
Call to undefined method Illuminate\Database\Query\Builder::save()
I tried to save the User object first and then assign the relationship, but obviously this is not possible because of the NOT NULL constraint in the definition of the user table.
My workaround at the moment is to assign the relationship this way:
$user->social_provider_id = $socialProvider->id;
But I would like to use Eloquent's features.
How can I save a new model with a not nullable relationship, without having to assign IDs by myself?
Solution:
Like #Panagiotis Koursaris suggested, the solution is to use associate() instead of save()

Try this:
$user = new User();
$socialProvider = SocialProvider::find(1);
$user->socialProvider()->associate($socialProvider);
$user->save();

In User model, do this
public function socialProvider(){
return $this->hasOne('App/SocialProvider');
}

Related

Laravel 5.6 Eloquent integrity constraint violation when saving quiz record

I'm currently working on a quiz application with Laravel 5.6 and am having trouble with saving a new quiz record.
The two tables that are being inserted into are quizzes and user_quizzes. The quizzes table contains some basic quiz data such as:
quiz_name
quiz_description
quiz_pin
active
The user_quizzes table contains two foreign keys to reference which quiz belongs to a particular user.
user_id
quiz_id
The error is an integrity constraint violation when inserting into the user_quizzes table. It successfully inserts the quiz_id but the user_id is left as NULL. I am unsure how to ensure the user_id is also inserted as I'm using Eloquent.
The full error is:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user_id' cannot be null (SQL: insert into `user_quizzes` (`quiz_id`, `user_id`) values (6, ))
I am making use of the QuizController, Quiz Model and User Model for saving the record. Here is my store() method in the QuizController:
public function store(Request $request)
{
$validator = $request->validate([
'quiz_name' => 'required|max:30',
'quiz_description' => 'required|max:500'
]);
$quiz = new Quiz(
[
'quiz_name' => $request->get('quiz_name'),
'quiz_description' => $request->get('quiz_description'),
'active' => '0',
'quiz_pin' => '5555', // hard coded for now
]
);
$quiz->save();
$user = new User;
$user->quizzes()->save($quiz);
return redirect()->route('quiz_host.dashboard.manage-quizzes')->with('quizCreated', 'Whoa ' . Auth::user()->username . ', you have created a quiz! Now it\'s time to add some questions');
}
My User Model is a follows:
<?php
namespace App\Models;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'username', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
public function activation()
{
return $this->hasOne('App\Models\Activation');
}
public function profile()
{
return $this->hasOne('App\Models\Profile');
}
public function quizzes()
{
return $this->belongsToMany(Quiz::class, 'user_quizzes', 'user_id', 'quiz_id');
}
}
and my Quiz model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Quiz extends Model
{
protected $table = 'quizzes';
protected $fillable = ['quiz_name', 'quiz_description', 'active', 'quiz_pin'];
public function user()
{
return $this->belongsToMany(User::class, 'user_quizzes', 'quiz_id', 'user_id');
}
}
Any guidance as to what I'm doing wrong would be greatly appreciated.
Reviewing your code
This is your controller:
QuizController.php
public function store(Request $request)
{
// your validations.
// Storing the quiz.
$quiz->save();
// User instance.
$user = new User;
// Storing the relationship.
$user->quizzes()->save($quiz);
// Returning the view.
return redirect()->route('quiz_host.dashboard.manage-quizzes')->with('quizCreated', 'Whoa ' . Auth::user()->username . ', you have created a quiz! Now it\'s time to add some questions');
}
Now, the problem here is related to the $user object.
When you do this:
$user = new User;
You are createing an instance of the User class, but this object isn't persisted yet into the database, what this means is that this object doens't have an id yet. You can confirm this doing dd($user->id), this will return null.
That's why when you do this:
$user->quizzes()->save($quiz);
It throws the SQL error, because you are calling a method to store the $primaryKey (id) of the $user object in the pivot table. But given that the $user object doens't have a primary key is trying to store a null value instead.
Solution
Now, I don't really know what is your "use case", but I will assume that the $user is the logged-in one, so to relate properly the relationship replace this:
// creating a User instance.
$user = new User;
with this:
// Logged-in user.
$user = auth()->user();
This will use the auth facade to get the actual logged-in user and return the object. Given that is a registered user it will have a proper id.
Alternative
If your use case is different and you will relate the quiz to a different user, do this instead:
// Some other user
$user = User::find($someId); // $user = User::find(5); for example
or this, to create a completely new User instance and relating a quiz to it:
// A new User
$user = new User;
$user->fill($someData);
$user-save(); // this will assign a primary key (id) to the object.
Now you can attach the related model to it.
Side note
Your users m--------m quizzes is a many to many relationship.
So, as the documentation says, the proper way to store a relatioship between the two objects is the attach() method:
$user->quizzes()->attach($quiz->id);
This method will create a record in the intermediate table (pivot) with the ids of the $user and $quiz objects.
To make it clear, new User will only create user model object, it is still not committed to DB.
When we try to call $user->quizzes()->save($quiz) it is will try to add an entry in user_quizzes pivot table, but user_id is empty, because user is still not created.
So you have to create a user entry in DB by calling $user->save() before adding it to pivot table.
$quiz->save();
$user = new User;
$user->save();
$user->quizzes()->save($quiz);

Eloquent relationship where values are formatted differently

We've recently switched to a new permissions system on a project I am working on.
I have completed integration for this, eloquent relationships and all, when the requirements of the integration changed a little.
The permission system integrates with all of our systems across our infrastructure, and when it comes to referencing users from the Laravel project, the value in the permissions system is slightly different; in that it is prefixed with user-.
For example, a user with the username james in my users table is referenced as user-james in the permissions system table.
Is there any way to specify the value the eloquent relationship should look at?
I could just add a column to the users table to store the primary key of this user as it exists in the permissions table, but I wanted to see if there was a way to do this with eloquent.
If we consider relation is one - one we can do something like below:
First extend BelongsTo relation and change condition on where clause:
class CustomBelongsTo extends BelongsTo
{
/**
* #inheritDoc
*/
public function addConstraints()
{
if (static::$constraints) {
// For belongs to relationships, which are essentially the inverse of has one
// or has many relationships, we need to actually query on the primary key
// of the related models matching on the foreign key that's on a parent.
$table = $this->related->getTable();
$this->query->where($table.'.'.$this->otherKey, '=', 'user-'.$this->parent->{$this->foreignKey});
}
}
}
Then override belongsTo method on your model to use this custom relation.
class User extends Model {
protected $table = 'users';
public function permissions(){
return $this->belongsTo(Permission:class, 'username');
}
public function belongsTo($related, $foreignKey = null, $otherKey = null, $relation = null)
{
// If no relation name was given, we will use this debug backtrace to extract
// the calling method's name and use that as the relationship name as most
// of the time this will be what we desire to use for the relationships.
if (is_null($relation)) {
list($current, $caller) = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$relation = $caller['function'];
}
// If no foreign key was supplied, we can use a backtrace to guess the proper
// foreign key name by using the name of the relationship function, which
// when combined with an "_id" should conventionally match the columns.
if (is_null($foreignKey)) {
$foreignKey = Str::snake($relation).'_id';
}
$instance = new $related;
// Once we have the foreign key names, we'll just create a new Eloquent query
// for the related models and returns the relationship instance which will
// actually be responsible for retrieving and hydrating every relations.
$query = $instance->newQuery();
$otherKey = $otherKey ?: $instance->getKeyName();
return new CustomBelongsTo($query, $this, $foreignKey, $otherKey, $relation);
}
}
I hope this help.

Unable to associate models without manually assigning the foreign key in Laravel 5

I'm running into an issue when trying to associate a child model to a parent in Laravel 5.
I see in the documentation under the Inserting Related Models's "Associating Models (Belongs To)" section that I should be able to:
Create a child model instance: $comment = new Comment(['message' => 'A new comment.']);
Get the parent record: $post = Post::find(1);
Use the parent record's association method to save the child which should automatically relate it to the parent: $comment = $post->comments()->save($comment);
This makes sense to me conceptually, the problem I'm having is creating the child record without manually assigning the parent's id as a foreign key.
In my project, I have have a parent table Accounts and a child table Events. The Events schema has a foreign key constraint assigned for the Accounts id:
Schema::create('events', function(Blueprint $table)
{
$table->increments('id');
$table->integer('account_id')->unsigned();
$table->foreign('account_id')->references('id')->on('accounts');
$table->timestamp('date_of_donation');
$table->timestamp('date_last_donation_letter_sent');
$table->timestamps();
});
So when I go to create the Events instance that I'm planning on associating to the parent Accounts I get an exception thrown:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'account_id' cannot be null (SQL: insert into events (date_of_donation, date_last_donation_letter_sent, account_id, updated_at, created_at) values (2015-05-16, , , 2015-05-16 17:35:36, 2015-05-16 17:35:36))
I understand why the error is thrown because I'm not giving the Events instance a foreign key value.
I can get around it by getting the Accounts record first and creating the Events instance like so:
$account = $accounts->findOrFail($id);
$event = $events->create(['account_id' => $account->id, ...]);
But it seems like the whole point of the "Associating Models (Belongs To)" section is so that you don't have to do this.
Am I missing or misunderstanding something about how the associating functionality is supposed to work?
Update
Here's my Account model:
<?php namespace Hub;
use Illuminate\Database\Eloquent\Model;
class Account extends Model {
protected $fillable =[
'first_name',
'last_name',
'display_name',
'email',
'zipcode',
'phone_number'
];
public function donationEvents(){
return $this->hasMany('Hub\donationEvent');
}
}
And my Event model:
<?php namespace Hub;
use Illuminate\Database\Eloquent\Model;
class Event extends Model {
protected $fillable = [
'account_id',
'date_of_donation',
'date_last_donation_letter_sent'
];
public function donationItems(){
return $this->hasMany('Hub\DonationItem');
}
public function account(){
return $this->belongsTo('Hub\Account');
}
}
Also, here's the code that triggers the error:
Route::get('dev2', function ( Hub\Account $accounts, Hub\Event $events){
$account = $accounts->findOrFail(1);
// This is where I'm trying to create the child record but I get the exception
$event = $events->create(['date_of_donation' => '2015-05-16', 'date_of_last_donation_letter_sent' => '']);
$event = $account->events()->save($event);
return [$account, $event];
});
The thing is, create() instantiates a new model and directly saves it. So you actually save it twice. However the first time there is no foreign key set.
Instead of create() use fill() to set all the values without saving:
$event = $events->fill(['date_of_donation' => '2015-05-16', 'date_of_last_donation_letter_sent' => '']);
Or you can use the constructor as well (I personally prefer this solution)
$event = new Event(['date_of_donation' => '2015-05-16', 'date_of_last_donation_letter_sent' => '']);

How to get last insert id in Eloquent ORM laravel

I am performing a database operation with "Eloquent ORM in Laravel". I just want to take the last insert id (not the maximum id) in the database.
I searched to get last insert id in laravel Eloquent ORM, I got following link (Laravel, get last insert id using Eloquent) that is refer to get last insert id from following function "$data->save()".
But I need to get
"Model::create($data)";
My Query:
GeneralSettingModel::create($GeneralData);
User::create($loginuserdata);
How can I retrieve the last inserted id?
Like the docs say: Insert, update, delete
"You may also use the create method to save a new model in a single
line. The inserted model instance will be returned to you from the
method. However, before doing so, you will need to specify either a
fillable or guarded attribute on the model, as all Eloquent models
protect against mass-assignment.
After saving or creating a new model that uses auto-incrementing IDs,
you may retrieve the ID by accessing the object's id attribute:"
$insertedId = $user->id;
So in your sample:
$user = User::create($loginuserdata);
$insertedId = $user->id;
then on table2 it is going to be
$input['table2_id'] = $insertedId;
table2::create($input);
**** For Laravel ****
$user = new User();
$user->name = 'John';
$user->save();
//Getting Last inserted id
$insertedId = $user->id;
You could wrap your data in the insertGetId() method like this
$id = DB::table('table')->insertGetId( $data );
In this case the array $data would look something like this
$data = [ 'field' => 'data' , 'field' => 'data' ];
and if you’re using the DB façade you could just append the lastInsertID() method to your query.
$lastInsertedID = DB::table('table')->insert( $data )->lastInsertId();
$lastId = User::create($loginuserdata)->id;
As others said bfore me you may retrieve id by using
$model->id;
but only if you are using standard naming convention for eloquent. If you want to use other name for primaryKey column, eg:
class Users extends Model{
$primaryKey = 'userid';
}
you may retrieve last inserted id by calling
$model->userid;
It is described in: https://laravel.com/docs/master/eloquent#eloquent-model-conventions where one can find:
Eloquent will also assume that each table has a primary key column
named id. You may define a $primaryKey property to override this
convention.
In addition, Eloquent assumes that the primary key is an incrementing
integer value, which means that by default the primary key will be
cast to an int automatically. 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.
You may do it as,
public function store(Request $request,ModelName $obj)
{
$lastInsertedId = $obj->create($request->all())->id;
}
Hope this will help you.
This is my try:
$model_ins = Model::orderBy('id', 'desc')->take(1)->first();
And use $model_ins->id.
This code works with Laravel 5.3:
$upstatus = User::create($status);
return response()->json($upstatus);
User pages/site I was call data.id
This is an eloquent model:
$user = new Reports();
$user->email= 'david#example.com';
$user->save();
$lastInsertId = $user->id;
A solution using Query Builder:
$lastInsertId = DB::table('reports')->insertGetId(['email' => 'david#example.com']);
$user = (new user)->create($request->all()) ;
\Session::flash('message', 'Thanks , Your record No (' .$user->id . ') has been Successfully added');
This is my solution. I return the inserted object and get its ID or any other property.
This is an easier way that works on all of the ways that you inserted:
DB::getPdo()->lastInsertId()

Laravel 4 eloquent create doesn't work and fill does

I want to create an instance of model called Property using method create with Input::all() as parameter. Input contains only fillable fields. When using create method laravel raise this exception:
alpha.ERROR: exception 'Illuminate\Database\QueryException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`propathai`.`properties`, CONSTRAINT `properties_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE) (SQL: insert into `properties` (`slug`, `updated_at`, `created_at`) values (, 2014-07-30 10:21:42, 2014-07-30 10:21:42))' in /home/ricardo/CHH/propathai/vendor/laravel/framework/src/Illuminate/Database/Connection.php:555
This exception is happening because insert query is not populated with all input params.
{"bedrooms":"4","title":"ricardo","room_type":"apartment","type":"rent","furnished":"fully_furnished","aging":"new","user_id":"3"}
I tried creating new Property object and using fill() method and it does work.
Code not working
public function store()
{
$input = Input::all();
$input['user_id'] = Auth::user()->id;
$property = Property::create($input);
return Response::json(array('success' => true, 'redirect' => '/dashboard/properties/'.$property->id.'/edit-details'));
}
Code working
public function store()
{
$input = Input::all();
$input['user_id'] = Auth::user()->id;
$property = new Property;
$property->fill($input);
$property->save();
return Response::json(array('success' => true, 'redirect' => '/dashboard/properties/'.$property->id.'/edit-details'));
}
Model
protected $guarded = array('id','services');
protected $fillable = array('user_id','bedrooms','title','room_type','type','furnished','aging');
If anyone knows why it's happening let me know please.
Thanks.
Your slug is not being filled.
SQL: insert into `properties`
(`slug`, `updated_at`, `created_at`) values (, 2014-07-30 10:21:42, 2014-07-30 10:21:42)
It may be one of those:
1- Your slug has a different name attribute on your input form.
2- It's not on your fillable array.
3- It's either empty, null or being filtered.
ex: to filter empty inputs I do:
$input = array_filter(Input::all(),'strlen');
By the way, the way you are doing this is not the nicest of ways. Check this out:
http://laravel.com/docs/eloquent
Click on "one to many"
What it would look like on your app?
Your model User
public function properties(){
return $this->hasMany('Property');
}
Your model Property
public function user(){
return $this->belongsTo('User');
}
On your controller:
$user = Auth::user();
$input = Input::all();
$input['user_id'] = $user->id;
$user->properties()->create($input);
That would be a more 'Laravel' way to approach it.
Also, if you really wanna go PRO watch Laracasts by Jeffrey Way.
It's the best online resource for Laravel4 I've seen online.

Categories