Laravel 8: Multiple Select Option Does Not Send Data - php

I have two tables at db, one of them is named users which simply contains user information of website and the other one is tags which contains some hashtags that users can choose from them.
(just like Stackoverflow that a user can select multiple tags such as php, javascript & etc)
So in order to make this relationship between these two, I added this to User model:
public function tags()
{
return $this->belongsToMany(Tag::class);
}
And also this one to Tag model:
public function users()
{
return $this->belongsToMany(User::class);
}
And here is the select option on blade, and users can select multiple tags from db:
<select class="form-control BSinaBold" name="skills[]" id="skills" multiple>
#foreach(\App\Models\Tag::all() as $tag)
<option value="{{ $tag->id }}" {{ in_array($tag->id , Auth::user()->tags->pluck('id')->toArray()) ? 'selected' : '' }}>{{ $tag->name }}</option>
#endforeach
</select>
Then at the Controller, I added this in order to update data at tags table:
public function update(Request $request, $profile)
{
$validate_data = Validator::make($request->all(),[
'job' => 'nullable',
'stackoverflow' => 'nullable',
'github' => 'nullable',
'instagram' => 'nullable',
'linkedin' => 'nullable',
'website' => 'nullable',
'location' => 'nullable',
'skills' => 'array',
]);
$user = User::findOrFail($profile);
$user->update([
'job' => request('job'),
'stackoverflow' => request('stackoverflow'),
'github' => request('github'),
'instagram' => request('instagram'),
'linkedin' => request('linkedin'),
'website' => request('website'),
'location' => request('location'),
]);
$user->tags()->sync(request('skills'));
$user->save();
return view('profile');
}
And it works fine and perfect but the only problem is this line, that does not sync data at tags table:
$user->tags()->sync(request('skills'));
So I tried debugging and I found out that request('skills') is EMPTY!
So the question is, why it does not send any data to the Controller?
I would really appreciate any idea or suggestion from you guys...
Thanks in advance.

I think you define your model relation wrong.
Tag model should be:
public function user()
{
return $this->belongsTo(User::class);
}
and the User Model relations:
public function tags()
{
return $this->hasMany(Tag::class);
}
If you aim to establish a one-to-many relation.

Related

Best way to validate related data update in laravel 8

I have two models:
User:
id
Post:
id
user_id
belongsTo User
text
I want to update a Post record, which way to validate user_id is better?
Number 1
Pass user_id to controller with get method like:
Route::post('/post/edit/{user_id}/{post_id}', 'PostController#update')->name('post.update');
and validate it in controller:
public function update($user_id, $post_id, Request $request){
abort_if(!User::first($user_id), 404);
$request->validate([
'text' => 'string|...',
]);
Post::findOrFail($post_id)->update([
'user_id' => $user_id,
'text' => $request->text,
]);
Number 2
Pass user_id with hidden field with POST method like:
view:
<input type="hidden" name="user_id" value="{{ $user_id }}>
Routing:
Route::post('/post/edit/{post_id}', 'PostController#update')->name('post.update');
Controller:
public function update($post_id, PostUpdate $request){
Post::findOrFail($post_id)->update([
'user_id' => $request->user_id,
'text' => $request->text,
]);
}
PostUpdate request:
public function rules()
{
return [
'user_id' => 'required|exists:users,id',
'text' => 'string',
];
}
Which way is proper or better?
If the user is the authentified user, use the Auth::class
public function update($post_id, Request $request){
$request->validate([
'text' => 'string|...',
]);
Post::findOrFail($post_id)->update([
'user_id' => \Auth::id(),
'text' => $request->text,
]);
}
You can even make sure the post owner is the current user (which is better).
public function update($post_id, Request $request){
$request->validate([
'text' => 'string|...',
]);
Post::where('user_id', \Auth::id())->findOrFail($post_id)->update([
'text' => $request->text,
]);
}
//or (and this is the proper way to do it. It does the same amout of queries and is way easier to read/maintain).
public function update($post_id, Request $request){
$request->validate([
'text' => 'string|...',
]);
$user = \Auth::user();
$post = $user->posts()->findOrFail($post_id);
$post->text = $request->text;
$post->save();
}
No need for the user validation since all of that is handled at login and the rest is maintained through middleware.
Route:
Route::post('/post/edit/{post_id}', 'PostController#update')->middleware('auth')->name('post.update');
I suggest another way for you. Work with middlewares.
In the gate you can check the type of user, so users should be able to edit if they are the owner. In this case you can add some other rules just in one file and user everywhere you want.
In app/Providers/AuthServiceProvider.php define your gate:
Gate::define('update-post', function ($user, \App\Post $post) {
return $user->id === $post->user_id;
});
Then in your routes web.php:
Route::get('/edit/{post}', 'PostController#edit')
->name('edit_post')
->middleware('can:update-post,post');
Route::post('/edit/{post}', 'PostController#update')
->name('update_post')
->middleware('can:update-post,post');

Laravel pivot of users_filmscore, ¿How I can check if the user already has scored the film and how to update the relationship attributes?

So, I have a pivot table called user_filmscores, where the users can ''follow (watching, dropped, hold-on...)'' a film and add a rating on it.
In the userController I have this function:
public function userFilm(Request $request){
$data = $request->all();
$validator=Validator::make($data,[
'user_id' => 'required',
'film_id' => 'required',
'state' => 'required',
'score' => 'nullable'
]);
if($validator->fails()){
return response()->json([
'ok' => false,
'error' => $validator->messages(),
]);
}
$film = Film::find($data['film_id']);
$user = User::find($data['user_id']);
$filmId=$data['film_id'];
$userId=$data['user_id'];
//Check if the relationship exists (I tried many methods but always the same result with false)
/*$hasFilm = User::where('id', $data['user_id'])->whereHas('film', function ($q) use ($filmId) {
$q->where('id', $filmId);
})->exists();*/
$hasFilm = $user->film()->where('film_id', '=', $filmId)->exists();
/*$user->film()->sync($film->getKey(), [
'film_id' => $data['film_id'],
'user_id' => $data['user_id'],
'state' => $data['state'],
'score'=> $data['score']
]);*/
if(User::where('id', $userId)->where('id')){
$user->film()->attach($film->getKey(), [
'film_id' => $data['film_id'],
'user_id' => $data['user_id'],
'state' => $data['state'],
'score'=> $data['score']
]);
}else{
$user->film()->detach($film->getKey());
}
}
In the final part of the code, I want to check if the relationship between the user and the film exists, to make an action or another. But when I try to check if the relationship exists, it always returns me a false.
I thought to do it like, if there is a relationship, first delete the old relationship, and then create a new one.
I don't know if there is a better way to do it.
User model has this function:
public function film(){
return $this->belongsToMany('App\Models\Film', 'user_filmscores', 'user_id', 'film_id', 'state', 'score');
}
Film model has this function:
public function user(){
return $this->belongsToMany('App\Models\Film', 'user_filmscores', 'film_id', 'user_id', 'state', 'score');
}
Users table:
Films table:
User_filmscores table:

Laravel 8: Multiple Select Option Sends Empty Request

I have two tables at db, one of them is named users which simply contains user information of website and the other one is tags which contains some hashtags that users can choose from them.
I also created a table named tag_user that can store the tag_id and user_id like this image:
(just like Stackoverflow that a user can select multiple tags such as php, javascript & etc)
So in order to make this relationship between these two, I added this to User model:
public function tags()
{
return $this->belongsToMany(Tag::class);
}
And also this one to Tag model:
public function users()
{
return $this->belongsToMany(User::class);
}
And here is the select option on blade, and users can select multiple tags from db:
<select class="form-control BSinaBold" name="skills[]" id="skills" multiple>
#foreach(\App\Models\Tag::all() as $tag)
<option value="{{ $tag->id }}" {{ in_array($tag->id , Auth::user()->tags->pluck('id')->toArray()) ? 'selected' : '' }}>{{ $tag->name }}</option>
#endforeach
</select>
Then at the Controller, I added this in order to update data at tags table:
public function update(Request $request, $profile)
{
$validate_data = Validator::make($request->all(),[
'job' => 'nullable',
'stackoverflow' => 'nullable',
'github' => 'nullable',
'instagram' => 'nullable',
'linkedin' => 'nullable',
'website' => 'nullable',
'location' => 'nullable',
'skills' => 'array',
]);
$user = User::findOrFail($profile);
$user->update([
'job' => request('job'),
'stackoverflow' => request('stackoverflow'),
'github' => request('github'),
'instagram' => request('instagram'),
'linkedin' => request('linkedin'),
'website' => request('website'),
'location' => request('location'),
]);
$user->tags()->sync(request('skills'));
$user->save();
return view('profile');
}
And it works fine and perfect but the only problem is this line, that does not sync data at tags table:
$user->tags()->sync(request('skills'));
So I tried debugging and I found out that request('skills') is EMPTY!
So the question is, why it does not send any data to the Controller?
I would really appreciate any idea or suggestion from you guys...
Thanks in advance.
UPDATE #1:
If the skills array is not being posted correctly on the form submission, then there could just be a simple problem with the form. Are you posting normally or using an AJAX call? Do you have a conflicting field on the form called 'skills' like a hidden input? Is the skills field located within the <form> tag?
Otherwise, if the browser is in fact posting the skills correctly but just not being read correctly by the request (unlikely), how about you try to switch your request helper functions request() to use the $request object that was passed into your function. IDK, but maybe it will work differently by some chance since we can't see all your code.
Also note that the validation function isn't doing much of anything since nothing is required.
public function update(Request $request, $profile)
{
$user = User::findOrFail($profile);
$user->update([
'job' => $request->input('job', null),
'stackoverflow' => $request->input('stackoverflow', null),
'github' => $request->input('github', null),
'instagram' => $request->input('instagram', null),
'linkedin' => $request->input('linkedin', null),
'website' => $request->input('website', null),
'location' => $request->input('location', null),
]);
$user->tags()->sync($request->input('skills', []));
// I don't think you need this since update & sync trigger saving
// $user->save();
return view('profile');
}
If you want to see all the data getting posted you can just dump or log the data for debugging:
public function update(Request $request, $profile)
{
Log::debug($request->all());
dd($request->all());
...

Saving foreign key user_id to posts table

I'm developing a laravel application with user and post model and i'm getting an error of:
Field 'user_id' doesn't have a default value
I have set up relationships for both models. Post belongs to user and a user can have as many posts. The user_id is not being saved to the post table in the database.
Post Controller:
class PostController extends Controller
{
public function postCreatePost(Request $request){
$this->validate($request, [
'body' => 'required'
]);
$post = new Post([
'body' => $request->input('body')
]);
$post->save();
return redirect()->route('dashboard');
}
Route:
Route::post('/createpost', [
'uses' => 'PostController#postCreatePost',
'as' => 'post.create'
]);
You need to specify user_id:
$post = new Post([
'body' => $request->input('body'),
'user_id' => auth()->user()->id
]);
Or you could use relationship:
$user = auth()->user();
$user->posts()->create(['body' => $request->input('body')]);
Also, don't forget to add user_id to the $fillable array in the Post model.
The reason is you are not giving a user_id when saving a post.
One solution is.
$post = new Post([
'body' => $request->input('body'),
'user_id' => $your_use_id
]);

Yii2 Many to Many with Self - filter through grid view (no attribute?)

I've used the Gii AJAX Crud generator, and I'm being driven up a wall by my own stupidity. I am using Yii 2 and want to search with many to many, on a table that has that relation with ITSELF in a junction table, with the Grid View.
table tag (id, name).
table tag_child (parent_id, child_id)
Class Tag
...
public function getParents()
{
return $this->hasMany(self::className(), ['id' => 'child_id'])
->viaTable('tag_child', ['parent_id' => 'id']);
}
public function getChildren()
{
return $this->hasMany(self::className(), ['id' => 'parent_id'])
->viaTable('tag_child', ['child_id' => 'id']);
}
And in my grid-view /columns:
[
'class' => '\kartik\grid\DataColumn',
'attribute'=>'name',
],
[
'class' => '\kartik\grid\DataColumn',
'label' => 'Tag Type',
'value' => function($tag) {
return $tag->displayTagTypes();
},
'attribute' => 'tagTypes'
],
TagQuery.php
...
public $tagTypes;
public function search($params)
{
$query = Tag::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params);
if (!$this->validate()) {
// $query->where('0=1');
return $dataProvider;
}
$query->joinWith('parents p');
$query->andFilterWhere(['id' => $this->id]);
$query->andFilterWhere(['like', 'tag.name', $this->name]);
return $dataProvider;
}
I'm able to display the results in my index table with that value function, but my Tag filter isn't able to search by tagTypes. How do I populate that?
As an example, when it's not many to many, I can use set my attribute to 'joinedTableName.value' and it works as soon as I add a $query->orFilterWhere('like', 'parent.name', $this->id) or whatever. But I'm at a loss now...
Declare $searchModel = new TagQuery() in your controller, then pass the $searchModel to the view and include it in the GridView options as 'filterModel' => $searchModel.
Either that, or you can do really custom filters using specific filterTypes and filter logic for each column.
You declare public tagType in the query model, but you don't do anything with it. $query->andFilterWhere(['like', 'tag.name', $this->tagType]);

Categories