Laravel DB relations - laravel

I'm trying to connect users table with user_info. I've found some posts about it and it works, but I've troubles with user_info->id autoincrement.
If I set it to:
$table->increments('id');
I'm getting error that no default value is set, it works only if I add
$table->integer('id')->nullable($value = true);
Here is what I got for now:
User model
public function userinfo()
{
return $this->hasOne('App\Userinfo');
}
Userinfo model
public function user()
{
return $this->belongsTo('App\User');
}
Migration/table structure
public function up()
{
Schema::create('user_info', function (Blueprint $table) {
//There is a problem, if I don't add user_id it says that user_id doesnt exists, so I created user_id and decided to set id to autoincrement but it doesnt works so I made it like that
$table->integer('id')->nullable($value = true);
$table->foreign('id')->references('id')->on('users');
$table->integer('user_id');
$table->string('avatar',255)->default('notset.jpg')->nullable($value = true);
$table->string('looking_for',255)->nullable($value = true);
$table->string('steam_nick',40)->nullable($value = true);
$table->integer('age')->length(3)->nullable($value = true);
$table->integer('isFriendly')->length(2)->nullable($value = true);
$table->integer('isToxic')->length(2)->nullable($value = true);
$table->integer('isLeader')->length(2)->nullable($value = true);
$table->integer('isFunny')->length(2)->nullable($value = true);
$table->integer('isSkilled')->length(2)->nullable($value = true);
$table->text('description')->nullable($value = true);
$table->timestamps();
});
}
Finally UserinfoController
public function update(Request $request, $id)
{
$user = User::findOrFail($id); //Get role specified by id
$this->validate($request, [
'steam_nick'=>'max:120',
'looking_for'=>'required',
'avatar'=>'',
'description'=>'max:450',
'age'=>'min:2'
]);
$input = $request->only(['steam_nick', 'looking_for', 'description','avatar','age']); //Retreive the name, email and password fields
$userinfo = new Userinfo();
if ($userinfo->user_id == null ) {
$userinfo->steam_nick = $request->steam_nick;
$userinfo->looking_for = $request->looking_for;
$userinfo->description = $request->description;
$userinfo->avatar = $request->avatar;
$userinfo->age = $request->age;
$user->Userinfo()->save($userinfo);
} else {
$user->Userinfo()->update($input);
}
}
Phpmyadmin
img
All I need is to set id in user_info same as id in users. I don't need user_id, but couldn't make it works other way, but I'm sure it's wrong.

Okay, let's break it down piece by piece.
First, what I like to do is specifically declare what my table column is as the second argument on any hasOne and belongsTo in a model. It's not strictly necessary since Laravel does some voodoo magic to guess what the table name is - but I recommend it because it makes the relationship very clear. So, I would do this on the user model:
public function userinfo()
{
return $this->hasOne('App\Userinfo', 'user_id'); // it references user_id on your user_info table.
}
Next, let's look at your user_info migration. You were having problems with your primary key id because you were trying to set it as integer when it needs to be increments. We'll tweak the first three lines:
$table->increments('id')->unsigned();
$table->integer('user_id');
$table->foreign('user_id')->references('id')->on('users'); // foreign references the user_id field in this table, since that's what's making the connection between users and users_info, we point back to the user's id
At this point, if you are able, I recommend php artisan migrate:fresh --seed. It might help with any weird database structures you have prior. This is optional, though. You can try it if it doesn't work straight away.
Now in the controller, I noticed that you were previously referencing $user->Userinfo()->update($input);, but you named the relationship userinfo() in your user model. This is probably one of the things that was giving you an issue. They must match. Try setting up your controller like so:
public function update(Request $request, $id)
{
$user = User::findOrFail($id); //Get role specified by id
$this->validate($request, [
'steam_nick'=>'max:120',
'looking_for'=>'required',
'avatar'=>'',
'description'=>'max:450',
'age'=>'min:2'
]);
$input = $request->only(['steam_nick', 'looking_for', 'description','avatar','age']); //Retreive the name, email and password fields
// Does this user have an info record? If not, create it, else update it.
if (is_null($user->userinfo)) {
// Since you already collected the data in the $input variable, we simply array_merge it with the user's id
Userinfo::create(array_merge([
'user_id' => $user->id,
], $input));
} else {
$user->userinfo()->update($input);
}
return redirect()->back();
}
So, basically what we are doing is checking to see if the user has a related record in the user_info table. If not, create it with the data. If so, simply update it. Since in our migration we declare that user_id cannot be null, we have to grab the user's id off the $user variable, and you've already grabbed all of the necessary data for the user_info table and put it in the $input variable, we simply array_merge() those two arrays when creating the user's info record.
Otherwise, if the related record already exists, we simply update the user_info record using our relationship ->userinfo().
Try that and let me know how it works.

Related

How to add to my Index/Show, so that it displays a table, from another table. (Laravel)

First post here, hope all is well with everyone! I am working on my senior project and struggling with this concept. Maybe it isn't possible, or it is and I'm thinking about it the wrong way.
I currently have this- This displays the student, with all its foreign keys in the other tables. In settings, there is a foreign key 'conversion_id.', I would like settings on the student call(code below) to ALSO display the conversion table, from the FK in settings.
$student = Student::with('studentIntroSurveys', 'settings', 'giftsSurveyResults',
'studentGiftSurveys', 'devotionals', )->get();
If this does not make sense, I am sorry. I am still l new to the language.
(Code to display students. It shows settings, but not the conversions within settings.)
$student = Student::with('studentIntroSurveys', 'settings', 'giftsSurveyResults',
'studentGiftSurveys', 'devotionals', )->get();
if (!$student) {
return response('No Data', 400);
} else {
return response($student);
}
I would like it to display this settings, but WITH the conversion_id table!!
What it displays =
settings: { setting_id: 4, student_id: 1, dark_mode: 1, conversion_id: 1,
notification_enabled: 1, notification_time: "08:00:00" } `
my has-one method =
public function settings()
{
$settings = $this->hasOne(Settings::class, 'student_id', 'student_id');
return $settings;
}
You can use the dot syntax for nested eager loading: 'settings.conversation' e.g.
$student = Student::with('studentIntroSurveys', 'settings.conversation', 'giftsSurveyResults', 'studentGiftSurveys', 'devotionals', )->get();
Alternatively, you could set up a belongsToMany relationship on the Student model and use settings as the pivot table:
public function conversations()
{
return $this->belongsToMany(Conversation::class, 'settings')
}
Just a few FYIs:
Your if statement is never going to return the 400 response as $student is always going to be a collection. You could instead do if ($student->isEmpty()).
I would also recommend changing the variable to $students as it will be a collection of students rather than a single student.
You can simple your settings relationship by removing the temporary variable and just returning the relationship:
public function settings()
{
return $this->hasOne(Settings::class, 'student_id', 'student_id');
}

Laravel polymorphic type not stored correctly

I'm using Backpack to create an Admin Panel for my project. I have a SoldProduct model (basically a physical item that has been sold to a customer), a Shoe model and a Sweatshirt model.
There is a polymorphic relationship between them in order to have a separate table for each one of them, avoiding the repetition of the columns that they share and also avoiding the (kinda ugly) solution of storing them ina big table that has got all the necessary fields that would remain partially null based on the type of product it's being stored.
Since I'm using Backpack's CRUD, I created a custom store method that creates the specific types of product and then creates a SoldProduct object linked to the specific one (by filling the productable_type ansd productable_id fields).
The problem is that while productable_id is stored correctly, in the productable_type field instead of storing for example "App\Models\Shoe" I keep getting "App\Models\SoldProduct" (the parent model's name), and I don't know where it's getting it from. The data is passed correctly to the "final" store method, but in the process of storing it gets modified in "App\Models\SoldProduct".
Any clue on why this might be happening? Thanks everybody.
Here's my code
public function store(Request $request) {
$this->crud->setRequest($this->crud->validateRequest());
$productable_type = $request->request->get('productable_type');
switch ($productable_type) {
case "Shoe":
$product = new Shoe;
$product->imprinted_code = $request->request->get('imprinted_code');
$product->size = $request->request->get('shoe_size');
$product->sole = $request->request->get('sole');
$product->save();
$product_id = $product->id;
$this->crud->addField(['type' => 'hidden', 'name' => 'productable_id']);
$this->crud->getRequest()->request->add(['productable_id' => "$product_id"]);
$this->crud->getRequest()->request->add(['productable_type' => "App\Models\\$productable_type"]);
break;
// repeat for Sweatshirt too
}
$this->removeField('imprinted_code', $request);
$this->removeField('shoe_size', $request);
$this->removeField('sole', $request);
$this->removeField('sweatshirt_size', $request);
$this->removeField('shirt_size', $request);
$this->removeField('pants_size', $request);
$this->crud->unsetValidation();
return $this->traitStore();
}
sold_products_table
Schema::create('sold_products', function (Blueprint $table) {
$table->id();
$table->foreignId('product_model_id')->constrained('product_models');
$table->foreignId('owner_id')->nullable()->constrained('users')->onUpdate('cascade');
$table->integer('productable_id')->nullable();
$table->string('productable_type')->nullable();
$table->string('note')->nullable();
$table->timestamps();
});
SoldProduct
public function productable() {
return $this->morphTo();
}
Shoe
public function product() {
return $this->morphOne('App\Models\SoldProduct', 'productable');
}
Sweatshirt
public function product() {
return $this->morphOne('App\Models\SoldProduct', 'productable');
}

Eloquent relationship: return just one value not entire row

In my laravel project I built a comment section and want to display the names of the people commenting beside their comment.
Initially I have just the userID, so I built a relationship (hasOne) linking the comment table (comment & authorID) to the users table (id (authorID) & username)
Comment.php (model) is:
[..]
public function author()
{
return $this->hasOne(User::class, 'id', 'Author');
}
The User.php model is:
<?php
[..]
public function author()
{
return $this->hasMany(Comment::class, 'Author', 'id');
}
In the controller I get the data with:
$comments= Comments::where('LinkID', (string) $id)->with('author')->orderBy('updated_at', 'ASC')->get()->all();
This works but it gives me the entire row of the user per comment. For security reasons I just want to return the 'name' field of the row (username) without the rest (email, timestamps etc.).
How can I achieve this?
please try:
$comments= Comments::where('LinkID', (string) $id)->with(['author' => function ($q) {
$q = $q->select('name', 'id');
return $q;
}
])->orderBy('updated_at', 'ASC')->get()->all();
or another way:
$comments= Comments::where('LinkID', (string) $id)->with('author:id,name')->orderBy('updated_at', 'ASC')->get()->all();
see eager loading (section Eager Loading Specific Columns)
https://laravel.com/docs/7.x/eloquent-relationships#eager-loading
note that including 'id' is necessary because it 's responsible for the relation

why one table is updating and the other not in laravel?

When I click the edit button, I am trying to bring data from two tables and them update them together. One is user table and other on is user detail table.
when I update the information, user table is getting updated but user_detail table is not getting updated rather creating a new row. how to solve that?
The same code is working for one table while it is not working for the other.
This is my update function.
public function update(Request $request, $id)
{
// $this->validate($request,[
// 'title'=>'required',
// 'body'=>'required',
// ]);
//$user=new User;
$user = User::find($id);
$user->student_no=$request->input('student_no');
$user->email=$request->input('email');
$user->save();
$user_detail=new Profile;
$user_detail = Profile::find($id);
$user_detail->student_no=$request->input('student_no');
$user_detail->profile_pic=$request->input('CID');
$user_detail->CID=$request->input('CID');
$user_detail->DOB=$request->input('DOB');
$user_detail->mobile_no=$request->input('mobile_no');
$user_detail->joining_year=$request->input('joining_year');
$user_detail->graduation_year=$request->input('graduation_year');
$user_detail->program_id=$request->input('programe_id');
$user_detail->work_experience=$request->input('work_experience');
$user_detail->save();
return redirect('/dashboard')->with('success','Post Updated');
The user table is getting updated but user_detail is not.
remove line,
$user_detail=new Profile;
and in line,
$user_detail = Profile::find($id);
here you can't do like this. You have to do something like this
$user_detail = Profile::where('user_id','=',$id)->first();
then your code should look like this,
public function update(Request $request, $id)
{
// $this->validate($request,[
// 'title'=>'required',
// 'body'=>'required',
// ]);
//$user=new User;
$user = User::find($id);
$user->student_no=$request->input('student_no');
$user->email=$request->input('email');
$user->save();
$user_detail = Profile::where('user_id','=',$id)->first();
$user_detail->student_no=$request->input('student_no');
$user_detail->profile_pic=$request->input('CID');
$user_detail->CID=$request->input('CID');
$user_detail->DOB=$request->input('DOB');
$user_detail->mobile_no=$request->input('mobile_no');
$user_detail->joining_year=$request->input('joining_year');
$user_detail->graduation_year=$request->input('graduation_year');
$user_detail->program_id=$request->input('programe_id');
$user_detail->work_experience=$request->input('work_experience');
$user_detail->save();
return redirect('/dashboard')->with('success','Post Updated');
Delete $user_detail=new Profile;
Use relations for update user details.
Read about $request->all() and Mass Assignment in Laravel.
Make form validation in separate files.
$user_detail = Profile::find($id);
in this line you are checking the profile data with the primary key of the profile table which might does not exist
You can try with
$user_detail = Profile::where('user_id',$id)->first();
user_id is the fk on profiles table from user id.
You should remove line $user_detail=new Profile;
and update line $user_detail = Profile::find($id); as following
$user_detail = Profile::where('user_id',$id)->first()
there two three things with your query.
$user_detail=new Profile;
$user_detail = Profile::find($id);
first of all no need for new profile line . It just made new object but you don't need that sine your going to update someone profile,
Now print your second line response weather your getting any response from it and also is it correct one because your finding id not user_id .
otherwise there is no problem with that.
hope you will find solution in this.

How to update field when delete a row in laravel

Let I have a table named customer where customer table has a field named deleted_by.
I implement softDelete in customer model. Now I want to update deleted_by when row delete. So that I can trace who delete this row.
I do search on google about it But I don't found anything.
I use laravel 4.2.8 & Eloquent
You may update the field using something like this:
$customer = Customer::find(1); // Assume 1 is the customer id
if($customer->delete()) { // If softdeleted
DB::table('customer')->where('id', $customer->id)
->update(array('deleted_by' => 'SomeNameOrUserID'));
}
Also, you may do it in one query:
// Assumed you have passed the id to the method in $id
$ts = Carbon\Carbon::now()->toDateTimeString();
$data = array('deleted_at' => $ts, 'deleted_by' => Auth::user()->id);
DB::table('customer')->where('id', $id)->update($data);
Both is done within one query, softDelete and recorded deleted_by as well.
Something like this is the way to go:
// override soft deleting trait method on the model, base model
// or new trait - whatever suits you
protected function runSoftDelete()
{
$query = $this->newQuery()->where($this->getKeyName(), $this->getKey());
$this->{$this->getDeletedAtColumn()} = $time = $this->freshTimestamp();
$deleted_by = (Auth::id()) ?: null;
$query->update(array(
$this->getDeletedAtColumn() => $this->fromDateTime($time),
'deleted_by' => $deleted_by
));
}
Then all you need is:
$someModel->delete();
and it's done.
I would rather use a Model Event for this.
<?php
class Customer extends \Eloquent {
...
public static function boot() {
parent::boot();
// We set the deleted_by attribute before deleted event so we doesn't get an error if Customer was deleted by force (without soft delete).
static::deleting(function($model){
$model->deleted_by = Auth::user()->id;
$model->save();
});
}
...
}
Then you just delete it like you would normally do.
Customer::find(1)->delete();
I know this is an old question, but what you could do (in the customer model) is the following....
public function delete()
{
$this->deleted_by = auth()->user()->getKey();
$this->save();
return parent::delete();
}
That would still allow the soft delete while setting another value just before it deletes.

Resources