Best way to validate related data update in laravel 8 - php

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');

Related

Concaternate Array value with Ternary Operator result

I'm using PHP 7 and Laravel 6. I got errors when I made a user request rule and used it in user controller. The request rule I made is to be reusable in create and update function, so if i pass the id of user, it will validate the unique of user except that id. But if not, it will search all the ids and validate if it's unique. I follow BaM solution, here: https://stackoverflow.com/a/24205849
This my UserRequest.php:
public static function rules ($id=0, $merge=[]) {
return array_merge(
[
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users' . ($id ? ",$id" : ''),
'phone_number' => 'required|string|min:9|max:10|unique:users' . ($id ? ",$id" : ''),
'user_img' => 'required|mimes:jpeg,jpg,png,gif|max:10000',
],
$merge);
}
This is my UserController:
public function store(Request $request)
{
$extend_rules = [
'pass' => 'required|string|min:8',
];
$validator = Validator::make($request->all(), UserRequest::rules($extend_rules));
if ($validator->fails())
{
return redirect()->back();
}
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->pass),
'phone_number' => $request->phone_number,
'user_img' => $request->user_image->store('user_img'),
]);
$user->save();
Session::flash('message', 'Your account is successfully created !');
Session::flash('alert-class', 'alert alert-success');
return redirect()->route('users.index');
}
And I got this errors:
ErrorException: Array to string conversion
I tried to search for solutions but couldn't seem to find anything much my case.
If anyone know, please help me!
That's because you're just passing an array while the method accept two different type of parameter
$validator = Validator::make($request->all(), UserRequest::rules(0, $extend_rules)); // <-- you need to either pass 1 or 0 for the id and then $extended rules
// here is your method signature
public static function rules ($id=0, $merge=[]) {

Laravel saving data to two locations

I'm working on a larvel project where the user can create appointments. In addition I've created another model called clients so when a user creates an appointment the users "client" data is saved.
In my appointments controller I have the following: -
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
]);
//create appointment
$apt = new Appointment;
$apt->name = $request->input('name');
$apt->user_id = auth()->user()->id;
$apt->save();
//create client
$client = new Client;
$client->first_name = $request->input('name');
$client->user_id = auth()->user()->id;
$client->save();
return redirect('/appointments')->with('success', 'Appointment created');
}
When saving the data it works and stores the data in the clients table however I know this isn't the cleanest way of saving the data, but what is the "laravel" way of doing this?
There's nothing wrong with your code. It's totally fine keeping it that way.
I prefer to say Model::create() to create models in one statement.
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
]);
Appointment::create([
'name' => request('name'),
'user_id' => auth()->id()
]);
Client::create([
'first_name' => request('name'),
'user_id' => auth()->id,
]);
return redirect('/appointments')->with('success', 'Appointment created');
}
You can also use tap() function:
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
]);
tap(Appointment::create(['name' => request('name'), 'user_id' => auth()->id()]), function ($newAppoinment) {
Client::create([
'first_name' => $newAppoinment->name,
'user_id' => auth()->id,
]);
});
return redirect('/appointments')->with('success', 'Appointment created');
}
Or the best approach could be using model events:
class Appointment extends Model
{
public static function boot()
{
parent::boot();
static::created(function ($appointment) {
Client::create([
'user_id' => $appoinment->user_id,
'first_name' => $appoinment->name,
])
});
}
}

How to get id When Using Implicit Route Model Binding, i need it in unique validation

// in the validation section "alias" field should be unique so i need this NursingHome object id(primary key) to force validation to not to check for this id.
I have checked it with $nursinghome->getKey() method but no success.
public function update(Request $request, NursingHome $nursinghome)
{
$request->validate([
'name' => 'required|string|max:255',
'address' => 'nullable|string',
'alias' => 'required|string|unique:nursing_home,'.$nursinghome->id,
]);
$data = $request->all();
$data['updated_by'] = Auth::guard('api')->id();
$nursinghome->update($data);
return response()->json($nursinghome, 200);
}
There is a know issue disscussed in laravel github, that if your model has two words like NursingHome the it is not injected in controller:
public function update(Request $request, $id){
$nursinghome = NursingHome::find($id); //now you will get $nursinghome->id
$request->validate([
'name' => 'required|string|max:255',
'address' => 'nullable|string',
'alias' => 'required|string|unique:nursing_home,'.$nursinghome->id,
]);
$data = $request->all();
$data['updated_by'] = Auth::guard('api')->id();
$nursinghome->update($data);
return response()->json($nursinghome, 200);
}
If your model having two or more words, you have to use only small letters.

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
]);

Laravel 5.2 Using Associate to Update a BelongsTo Relationship

I'm using Route Model Binding to get a User instance then update it if validation passes, and then update the associated belongs to relationship between User and Profile, but I keep getting an error. The update occurs on the User, but fails on updating the Profile. From what I've understood from the docs this appears to be correct. I can access Profile data using $user->profile so the relationship appears to be okay in the User and UserProfile models.
Can anyone see what is wrong with this controller action:
public function update(Request $request, User $user)
{
$this->validate($request, [
'username' => 'required|max:32|unique:users',
'email' => 'required|email|max:128|unique:users',
'first_name' => 'required',
'last_name' => 'required',
'phone_number' => 'regex:/^([0-9\s\-\+\(\)\.]*)$/',
]);
$user->update($request->all());
$profile = new UserProfile($request->all());
// Also tried:
//$profile = UserProfile::where(['user_id' => $user->id])->first();
$user->profile()->associate($profile);
$user->save();
return response()->json([
'message' => trans('user.updated'),
]);
}
Error
BadMethodCallException in Builder.php line 2161:
Call to undefined method Illuminate\Database\Query\Builder::associate()
User Model Relationships
/**
* A user has-one profile.
*
* #return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function profile()
{
return $this->hasOne('App\UserProfile');
}
UserProfile Model Relationship
/**
* A user profile belongs to a user.
*
* #return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function user()
{
return $this->belongsTo('App\User');
}
Solution
$user->fill($request->all())->save();
$profile = UserProfile::where('user_id', $user->id)->first();
$profile->fill($request->all());
$user->profile()->save($profile);
You must retrieve or create new profile entity first and put it in $profile. Also, you have One-to-one relation here, so you should save your user's profile like this:
$user->profile()->save($profile);
Change your code to this:
public function update(Request $request, User $user)
{
$this->validate($request, [
'username' => 'required|max:32|unique:users',
'email' => 'required|email|max:128|unique:users',
'first_name' => 'required',
'last_name' => 'required',
'phone_number' => 'regex:/^([0-9\s\-\+\(\)\.]*)$/',
]);
$profile = UserProfile::create($request->all());
$user->profile()->associate($profile);
$user->save();
return response()->json([
'message' => trans('user.updated'),
]);
}

Categories