I'm beginner in laravel and I want to update multiple checkboxes in database ..
when I click at update button automatically my inputs show old value also my permissions are checked by old value to update it ..
relation between user and permission is manytomany .. I have another table named userpermissions who has id_user and id_permission
this is my update form in ( edit.blade.php)
<form action="{{ url('users/'.$user->id) }}" method="POST">
#csrf
#method('PUT')
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label>Name</label>
<input type="text" name="name" id="name" required class="form-control" value="{{ $user->name }}">
#error('name')
<ul class="alert"><li class="text-danger">{{ $message }}</li></ul>
#enderror
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Email</label>
<input type="email" name="email" id="email" required class="form-control" value="{{ $user->email }}">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
#foreach($permissions as $permission)
<input type="checkbox" name="data[]" value="{{ $permission->id }}"
<?php if( in_array($permission->id, $user->userPermissions->pluck('permission_id')->toArray())){ echo 'checked="checked"'; } ?>/>
{{ $permission->name }}
#if($loop->iteration % 3 == 0 ) <br> #else #endif
#endforeach
</div>
</div>
</div>
<div class="text-right mt-4">
<button type="submit" class="btn btn-primary"> Add</button>
</div>
</form>
and this is my controller where I think have a problem with methods :
edit function
public function edit(User $user)
{
$permissions = Permission::get();
return view('users.edit', compact('user','permissions'));
}
update function :
public function update(UserRequest $request,User $user)
{
$user->update(
$request->only('name', 'email')
);
$user->userPermissions()->save($request->input('data'));
return redirect()->back()->with('status','user updated !');
}
and this is my functio store :
public function store(UserRequest $request)
{
$this->validate($request, [
'name' => 'required',
'email'=>'required|email',
'password' => 'required|confirmed|min:6',
]);
$user = User::create(
$request->only('name', 'email', 'password')
);
$user->userPermissions()->createMany($request->input('data'));
return redirect()->back()->with('status','Utilisateur ajouté !');
}
Thanks for advance !
$user->userPermissions()->save($request->input('data'));
One important thing to understand here, is that save() on relation doesn't remove old values from pivot table, it just add more values to it(no distinction check). You need something like refresh functionality. Look at attaching\detaching or sync, second one is more convenient.
In first case before saving permissions you can do this
// remove all old permissions
$user->userPermissions()->detach();
// update them with new one
$user->userPermissions()->attach($request->input('data'));
In second case, which is less verbose then first one you just need to pass and array of permissions to user object.
// this will do both things which we did before
$user->userPermissions()->sync($request->input('data'))
But i encourage you to read the docs and ask questions after ;)
Another thing which i saw and its not related to the current topic is
$user->userPermissions->pluck('permission_id')->toArray()
you are using lazy load inside of foreach loop which means that on each iteration of the loop you are making a query to the database(N + 1 problem). You can preload/eager load userPermissions instead of loading them on a fly by declaring with relation in your User model like this
class User extends Model
{
/**
* The relationships that should always be loaded.
*
* #var array
*/
protected $with = ['userPermissions'];
...
}
and then in your User object will have userPermissions property which you can compare to permissions.
Hope that you get main idea and info was useful for you!
Related
I am a newbie in Laravel. I am building an app where people who attend training can download their softcopy of certificates. This is the flow I want to achieve
Index page displays a form of three inputs
Certificate Number (cert_no)
Certificate Type (cert_type)
Member Email (member_email)
On submit, if record exists, send an email which contains a token and route to the next page where you supply the token sent to your email.
Now my challenge is, how do I check the database to validate that one record meets ALL requirements?
Below is my form
<form method="POST" action="{{ route('checkExist') }}" enctype="multipart/form-data" data-form-validate="true" novalidate="novalidate">
#csrf
<div class="card bg-white shadow">
<div class="card-body pt-2">
<div class="input-group">
<label>Certificate No.</label>
<input type="text" name="cert_no" class="input input-md input-green input-block" value="{{ old('cert_no') }}" data-rule-required="true" data-msg-required="* Field cannot be empty" required>
</div>
<div class="input-group">
<label>Choose Type</label>
<select name="cert_type" class="input input-md input-green input-block" data-rule-required="true" data-msg-required="* Select an option" required>
#if ($errors->any())
<option value="{{ old('cert_type') }}">{{ old('cert_type') }}</option>
#endif
<option value="">---Select Option---</option>
<option value="Certificate 1">Certificate 1</option>
<option value="Certificate 2">Certificate 2</option>
</select>
</div>
<div class="input-group">
<label>Email</label>
<input type="email" name="member_email" class="input input-md input-green input-block" value="{{ old('member_email') }}" data-rule-required="true" data-msg-required="* Field cannot be empty" required>
</div>
<div class="input-group mt-1">
<input type="submit" class="btn btn-lg btn-cipm btn-block" value="Get Access">
</div>
</div>
</div>
</form>
Below is my Route
Route::post('/certificate', [App\Http\Controllers\CertificatesController::class, 'checkExist'])->name('checkExist');
and below is my function
public function checkExist(Request $request)
{
$this->validate($request, [
'cert_no' => 'required',
'cert_type' => 'required',
'member_email' => 'required|email'
]);
$cert_no = $request->Input('cert_no');
$cert_type = $request->Input('cert_type');
$member_email = $request->Input('member_email');
$certificate = Certificate::where('cert_no', '=', $cert_no)->where('cert_type', 'LIKE', $cert_type)->where('member_email', '=', $member_email)->get();
if (count($certificate) > 0)
{
return 'Yes';
}
else
{
return 'No';
}
}
And I get a 'No' returned, though I have a record in the database that meets the requirements
[UPDATE 1] Please note that the reason I have to use all three inputs to validate is because at some trainings, one cert_no can have two cert_type. Hence, the need to check if a cert_no AND cert_type AND member_email exists in the DB. Also note that I had accomplished this using normal PHP $query and it works.
[UPDATE 2] I removed ->where('cert_type', 'LIKE', $cert_type) from the function above and it works fine. So right now it checks if a cert_no AND member_email exists. But I need to also use the cert_type too. And the reason I am using LIKE is because the data stored under cert_type is a comma delimited array. Since field member_email is unique, two records of same email cannot exist, but one email can have multiple types of certificate.
[UPDATE 3] So, it is finally working. I really appreciate all efforts and contributions. I was missing out '%' sign so I changed the code form
$certificate = Certificate::where('cert_no', '=', $cert_no)->where('cert_type', 'LIKE', $cert_type)->where('member_email', '=', $member_email)->get();
to
$certificate = Certificate::where('cert_no', '=', $cert_no)->where('cert_type', 'LIKE', '%'.$cert_type.'%')->where('member_email', '=', $member_email)->get();
In laravel you already have a validatio to perform this action.
Checkout this.
https://laravel.com/docs/7.x/validation#rule-exists
With exists you can check if the particular data is available on database or not.
Does something like this work for you?
public function checkExist(Request $request)
{
$data = $request->validate([
'cert_no' => 'required',
'cert_type' => 'required',
'member_email' => 'required|email'
]);
$exists = Certificate::where($data)->exists();
if ($exists)
{
return 'Yes';
}
else
{
return 'No';
}
}
If not, dd($data) and see if the data coming from the front-end meets your requirements.
You can use following code for check valid query
dump($certificate->toSql(), $certificate->getBindings());
I am getting an error which says
"Property [id] does not exist on this collection instance. (View: C:\newXampp\htdocs\testUser\resources\views\Stock\edit.blade.php)"
This is my Controller
public function editStock(Stock $id)
{
//
$Stock = Stock::find($id);
return view('Stock.edit', compact('Stock', 'id'));
// return response($stock);
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Stock $stock
* #return \Illuminate\Http\Response
*/
public function updateStock(Request $request, $id)
{
$request->validate([
'id'=>'required',
'stock_name'=>'required',
'stock_qty'=>'required',
'stock_unit'=>'required',
'stock_price_per_kg'=>'required',
'stock_weight_per_qty'=>'required'
]);
$stock = Stock::find($id);
$stock->stock_name = $request->get('stock_name');
$stock->stock_qty = $request->get('stock_qty');
$stock->stock_unit = $request->get('stock_unit');
$stock->stock_price_per_kg = $request->get('stock_price_per_kg');
$stock->stock_weight_per_qty = $request->get('stock_weight_per_qty');
$stock->save();
return redirect('/Stock/index/')->with('success', 'Stock updated!');
}
These are my routes
//Route for Stock
Route::get('/Stock/createStock/', 'ChicController#createStock')->name('createStock');
Route::post('/Stock/createStock/', 'ChicController#storeStock')->name('storeStock');
Route::get('/Stock/index/', 'ChicController#indexStock')->name('indexStock');
Route::get('/Stock/edit/{id}', 'ChicController#editStock')->name('editStock');
Route::post('/Stock/edit/{id}', 'ChicController#updateStock')->name('updateStock');
Route::delete('/Stock/index/{id}', 'ChicController#destroyStock')->name('deleteStock');
This is my edit.blade.php
#extends('layouts.app')
#section('content')
<div class="row">
<div class="col-sm-8 offset-sm-2">
<h1 class="display-3">Update a Stock</h1>
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
<br />
#endif
<form method="post" action="{{ route('updateStock', $Stock->id) }}">
{{ csrf_field() }}
<div class="form-group">
<label for="stock_name">Stock Name:</label>
<input type="text" class="form-control" name="stock_name" value={{$Stock->stock_name }} />
</div>
<div class="form-group">
<label for="stock_qty">Stock Amount:</label>
<input type="number" class="form-control" name="stock_qty" value={{$Stock->stock_qty }} />
</div>
<div class="form-group">
<label for="stock_unit">Stock Unit:</label>
<select id="stock_unit" name="stock_unit" value={{$Stock->stock_unit}}>
<option value="Kg">Kg</option>
<option value="Qty">Qty</option>
</select>
</div>
<div class="form-group">
<label for="stock_price_per_kg">Price Per Kg:</label>
<input type="number" class="form-control" name="stock_price_per_kg" value={{$Stock->stock_price_per_kg }} />
</div>
<div class="form-group">
<label for="stock_weight_per_qty">Weight Per Qty:</label>
<input type="number" class="form-control" name="stock_weight_per_qty" value={{$Stock->stock_weight_per_qty }} />
</div>
<button type="submit" class="btn btn-primary">Update</button>
</form>
</div>
</div>
#endsection
I have tried everything, but I could not solve the problem. When I try to echo the $id in the edit Controller, it shows the correct $id. Hence, I do not know how to solve this, nor do I know why this is happening.
For your editStock method you are already receiving an instance of a Stock model matching the id from the route parameter due to the Implicit Route Model Binding that is taking place. When you pass that $id variable to Stock::find($id) you are passing a Model which is Arrayable. This causes find to become findMany thinking you are passing many ids. You end up with a Collection because of this:
public function editStock(Stock $id)
{
$Stock = Stock::find($id);
return view('Stock.edit', compact('Stock', 'id'));
}
$Stock is a Collection because of passing many to find. I would adjust this to only pass the $id variable to your view which is the Stock that you want.
public function editStock(Stock $id)
{
return view('Stock.edit', [
'Stock' => $id,
]);
}
Your view will now have a Stock variable which is the Stock that you wanted.
Laravel 5.8 Docs - Routing - Route Model Bindings - Implicit Bindings
Here is my routes
Route::get('add-members/{id}','MemberController#create');
Route::post('save-member/{id}','MemberController#store');
This is my code to show the create form
public function create($id)
{
$team=Team::find($id);
$users = User::doesntHave('teams')->whereHas('roles', function($role) {
$role->where('name', 'member');
})->get();
return view('members.create',compact('users','team'));
}
An this is my code to store it
public function store(Request $request,$id)
{
$team=Team::find($id);
dd($request->id);
$team->users()->attach($request->id);
return redirect('home');
}
and this is my blade file
#extends('layouts.app')
#section('content')
<form action="{{url('save-member',$team->id)}}" method="post" accept-charset="utf-8">
#csrf
<div class="form-group row">
<label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Select Member/s') }}</label>
<div class="col-md-6">
#foreach($users as $key => $user)
<input type="checkbox" name="id[]" value="{{$user->id}}">{{$user->email}}<br>
#endforeach
#error('member_id')
<span class="invalid-feedback" role="alert"><strong><font
color="red">{{ $message }}</font></strong></span>
#enderror
</div>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
#endsection
Now when i select none of the user and just click save button it will save the the user id as 1. After i am doing dd($request->id) it will show me the output 1. But in my form there is no users left or my form is empty.So where from 1 is coming. you can see this picture for clearify.
Please help me to solve this problems
You should be more specific with what data you are requesting from the Request:
$request->id; // could be an input named 'id' or a route parameter named 'id'
$request->input('id'); // is an input
$request->route('id'); // is a route parameter
You are running into a situation where you have a route parameter named id and potentially an input named id. Using the dynamic property of the Request, $request->id, will return the input id if it is there, if not it falls back to returning a route parameter named id.
Here is an article from the past that shows the issue with not being specific about what you are trying to get from the Request object:
asklagbox - blog - watch out for request
i have a input fields to insert names to the database at the moment it works like every time i enter one name it inserts and does not allow duplications the name but i want to enter multiple names comma separated this si what i have at the moment
front end
<form method="POST" action="{{route('store.names')}}">
#csrf
<div class="form-group row">
<label for="names"
class="col-md-4 col-form-label text-md-right">Add New Name
</label>
<div class="col-md-6">
<input id="names" type="text"
class="form-control #error('names') is-invalid #enderror" name="names"
value="{{ old('names') }}" autocomplete="names" autofocus>
#error('names')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
#enderror
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
Add Names
</button>
</div>
</div>
</form>
Controller
public function store(Request $request)
{
$validation = Names::create($this->validateRequest());
return back()->with('message','Added'));
}
private function validateRequest()
{
return request()->validate([
'names' => 'required|min:2|unique:Names',
]);
}
what this does is it inserts names of one person at a time how can i insert multiple people with comma separated with the validations intact
but this iss what i need
i have figured it needs to be somthing like this
$searchString = ',';
if( strpos($request->names;, $searchString) !== false )
{
// insert in to db separately
}else{
$names= Names::create($this->validateRequest());
}
you could override prepareForValidation to preprocess the data. In this case, it would split the string by your delimiter. Then you can apply array validation using the rules method or your own explicit validation call.
protected function prepareForValidation() {
$this->replace(['names' => explode(',', $this->names)]);
}
public function rules() {
return [
'name' => 'required|min:2|unique:Names',
'names.*' => 'name'
];
}
You can use the explode() PHP function to split the string given by the input field into an array. The array will give you more flexibility to store multiple names.
Example:
explode(", ", "peter, jhon, pete");
Adapt this to your code and you'll be fine!
For more informations about the explode() function you can read the PHP documentation: https://www.php.net/manual/en/function.explode.php
I'm trying to update my database using a form on my
edit.blade.php page as shown below. The edit part works correctly as the fields are filled in in the form as expected, however when i try to save, an error message of
Symfony \ Component \ HttpKernel \ Exception \ MethodNotAllowedHttpException
No message
is displayed. I have tried so many ways on how to fix it and I'm not sure where I'm going wrong. Hopefully it's something simple to fix?
edit.blade.php
#extends('layouts.app')
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
#section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<form method="post" action="{{ action('PostsController#update', $id) }}">
{{ csrf_field() }}
<input type="hidden" name="_method" value="PATCH" />
<h1>Edit Item</h1>
<div class="form-group">
<label for="item">Item:</label>
<input type="text" id="item" name="item" value="{{$post->item}}" class="form-control" required>
</div>
<div class="form-group">
<label for="weight">Weight (g):</label>
<input type="number" id="weight" value="{{$post->weight}}" name="weight" class="form-control">
</div>
<div class="form-group">
<label for="noofservings">No of Servings:</label>
<input type="number" id="noofservings" value="{{$post->noofservings}}" name="noofservings" class="form-control">
</div>
<div class="form-group">
<label for="calories">Calories (kcal):</label>
<input type="number" id="calories" name="calories" value="{{$post->calories}}" class="form-control">
</div>
<div class="form-group">
<label for="fat">Fat (g):</label>
<input type="number" id="fat" name="fat" value="{{$post->fat}}" class="form-control">
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
</div>
</div>
#endsection
PostsController.php
<?php
public function update(Request $request, $id)
{
$this->validate('$request', [
'item' => 'required'
]);
$post = Post::find($id);
$post->item = $request->input('item');
$post->weight = $request->input('weight');
$post->noofservings = $request->input('noofservings');
$post->calories = $request->input('calories');
$post->fat = $request->input('fat');
$post->save();
return redirect('/foodlog');
}
web.php
<?php
Route::get('edit/{id}', 'PostsController#edit');
Route::put('/edit', 'PostsController#update');
Post.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = [
'id',
'user_id',
'item',
'weight',
'noofservings',
'calories',
'fat',
'created_at'
];
}
My website is a food log application and this function is so that they can edit their log.
Any help is greatly appreciated!
Based on Michael Czechowski I edited my answer to make this answer better, The main problem is inside your routes:
Route::put('/edit/{id}', 'PostsController#update');
You have to add the id inside your route parameters either. Your update() function needs two parameters, first the form parameters from the formular and second the $id of the edited log entry.
The second problem is , the form method field is 'patch' and your route method is 'put'.
The difference between 'patch' and 'put' is:
put: gets the data and update the row and makes a new row in the database from the data that you want to update.
patch: just updates the row and it does not make a new row.
so if you want to just update the old row change the route method to patch.
or if you really want to put the data, just change the put method field in your form.
simply by : {{method_field('PUT')}}
Remember, the form's and the route's methods must be same. If the form's method is put, the route method must be put; and vice-versa.
The main problem is inside your routes:
Route::put('/edit/{id}', 'PostsController#update');
You have to add the id inside your route parameters either. Your update() function needs two parameters, first the form parameters from the formular and second the $id of the edited log entry.
The second one is inside your HTML template:
<input type="hidden" name="_method" value="PUT" />
To hit the right route you have to add the corresponding method to your route Route::put('/edit/{id}', 'PostsController#update');.
A possible last problem
<form method="post" action="{{ action('PostsController#update', $post->id) }}">
I am not sure how your template works, but $id is possible not set inside your template. Maybe try to specify the ID depending on your post. Just to make it sure the ID comes from the shown post.
Further suggestions
Best practice is to use the symfony built-in FormBuilder. This would make it easier to target those special requests like PUT, PATCH, OPTIONS, DELETE etc.