i cant pass the parameter id on the URI slim framework - php

i have an update form to update the product details :
<form action="{{ path_for('product.update', {id: product.id}) }}" method="get">
// another fields
<input type="hidden" name="_METHOD" value="PUT">
<input type="submit" name="submit" class="submit" value="update">
routes :
$app->get('/admin/product/edit/{id}', ['maimana\Controllers\AdminController','editupdateProduct'])->setName('product.edit');
$app->get('/admin/product/update/product/{id}', ['maimana\Controllers\AdminController','updateProduct'])->setName('product.update');
and the controller :
public function updateProduct($id, Request $request, Response $response){
$product = $this->product->where('id',$id)->first()->update([
'title' => $request->getParam('title'),
'category' => $request->getParam('category'),
'slug' => $request->getParam('slug'),
'description' => $request->getParam('description'),
'price' => $request->getParam('price'),
'image' => $request->getParam('image'),
'stock' => $request->getParam('stock')
]);
return $response->withRedirect($this->router->pathFor('admin.product'));
}
everytime i hit the update button, i cant pass the parameter is automatically and it turns PAGE NOT FOUND. but when i add the id manually it works and redirect back to admin.product.
please help me im getting stuck for about 4 days and i need this for my college task

Slim also uses Restful URLs it seems. When you have the _method set to 'put' in the form and sends the request, it will interpret it as a PUT request rather than a GET. Since this is your college task, I think I've helped you with 99% of the problem here.

Related

Laravel 8: Call to a member function notify() on string

I'm working with Laravel 8 to make a forum and now I wanted to send a notification to a user who has asked a question whenever someone answered that question.
So in order to do that, I added this to the Controller:
public function postAnswer(Request $request, $question, $asker) {
$validate_data = $request->validate([
'answer' => 'required',
]);
$answer = Answer::create([
'answer' => $request->answer,
'user_id' => auth()->user()->id,
'question_id' => $question,
]);
$asker->notify(new RepliedToThread($question));
return back();
}
So the $question is the question id and $asker is the user id of the person who has asked the question.
But now I get this error:
Call to a member function notify() on string
Now the interesting part is that, when I use auth()->user()->notify(new RepliedToThread($question));, it is working fine!
But I don't want to notify any authenticated user, I need to notify the person who has asked question, that's why I tried passding $asker...
So if you know how to solve this question, please let me know...
I would really appreciate any idea or suggestion from you guys.
Thanks in advance.
Blade:
<form action="{{ route('questions.answers', ['question' => $show->id, 'asker' => $show->user->id]) }}" method="POST" enctype="multipart/form-data">
#csrf
<div class="mb-4">
<textarea name="answer" id="answer" class="form-control BSinaBold" rows="7" placeholder="Write down answer"></textarea>
#error('answer')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
#enderror
</div>
</br>
<button type="submit" class="btn btn-primary">Send</button>
</form>
The problem is that asker is just an id, you should take the other information of the asker from the user table and after that pass your notification to the asker.
$asker = User::findOrFail($asker);
$asker->notify(new RepliedToThread($question));
Based on what you said, the problem is $asker. This parameter should be an instance from User model but the error implies it is string.
In your form change the form tag:
<form action="{{ route('questions.answers', ['question' => $show->id, 'asker' => $show->user]) }}" method="POST" enctype="multipart/form-data">
Remove the id after user.
make sure your Answer.php have use Notifiable; imported
ref link https://laravel.com/docs/8.x/notifications#using-the-notifiable-trait
then only you can call notify() function from Answer model instance
The problem is that $asker is just a string, its just the idvalue of the user.
I recommend that you use laravels route model binding. The passed parameters $querstion and $asker are automatically queried from the database.
https://laravel.com/docs/8.x/routing#route-model-binding
Afterwards you can access the question id normally and you have the notifiable user model.
public function postAnswer(Request $request, Question $question, User $asker) {
$validate_data = $request->validate([
'answer' => 'required',
]);
$answer = Answer::create([
'answer' => $request->answer,
'user_id' => auth()->user()->id,
'question_id' => $question->id,
]);
$asker = User::find($asker);
$asker->notify(new RepliedToThread($question));
return back();
}

Laravel how to pass null value in route parameter in front end

I have a form and I'm trying to pass null value into the $dev-id parameter but it doesnt work, instead ti will replace the dev id parameter with the $id as shown in my code at the bottom,
The reason i needed it to be null because I'm using this function for 2 different page but one of the page doesn't have $dev_id. any help is appreciated
HTML:
<form method="post"
action="{{ route('admin.developers.admins.edit.post',['admin' => $admin, 'dev_id' => null, 'id' => $id]) }}" autocomplete="off" novalidate>
web.php :
Route::post('developers/{dev_id?}/admins/{id}/edit', 'Admin\DeveloperAdminController#postEditDeveloperAdmin')->name('admin.developers.admins.edit.post')->where(['dev_id' => '[0-9]+', 'id' => '[0-9]+']);
You can pass those parametres in the form of hidden input and in controller use request to get those values
Add these to yur form
<input type="hidden" value="anything" name="dev_id">
Instead you are using vue use `v-model` instead of name and value
Now modify your form like this
<form method="post"
action="{{ route('admin.developers.admins.edit.post',['admin' => $admin,'id' => $id]) }}" autocomplete="off" novalidate>
Now you are access that using $request object in your logic and check if the value is null or not

Laravel NotAllowed

This is error says. Im inserting an static value for now in controller to check if the controller is okay. the code is below
protected function methodNotAllowed(array $others)
{
throw new MethodNotAllowedHttpException($others);
}
//IM JUST inserting static value for now to be able to check if inserting
DataController
public function update(Request $request, $name=null)
{
$insert = new leave([
'bio_id' => '10258',
'vacation_balance' => '25',
'sick_balance' => '25'
]);
$insert->save();
return view('pages/admin/data');
}
Route web.php
Route::post('admin/pages/admin/data', 'Admin\DTRDataController#update');
data.blade.php
<form action="{{url('admin/pages/admin/dtrdata')}}" method="post">
{{ csrf_field() }}
<input type="hidden" name="_method" value="PATCH">
<input type='text' class='total_undertimes' name='total_undertimes' id='total_undertimes' style='width:70px' />
<input type="submit" class="btn btn-primary" value="Edit" />
</form
What is ther error in my code
Remove
<input type="hidden" name="_method" value="PATCH">
line from your view. This makes your FORM submitted as PATCH method.
FYI:
methodNotAllowed exception throws when request using wrong method.
Ex:) When u POST to URL that is configured as GET in your Route file. In your situation you are requesting POST url using PATCH method.
You are showing the route for the URI admin/pages/admin/data but your form is going to admin/pages/admin/dtrdata. I am not sure what that 'dtrdata' URI is but it doesn't accept the PATCH method.
admin/pages/admin/data != admin/pages/admin/dtrdata
Your Form Url and route are different :
route : admin/pages/admin/data
form : admin/pages/admin/dtrdata
you need to do two correction in your code.
your Form Url and route are different :
route url : admin/pages/admin/data
form_url : admin/pages/admin/dtrdata
methodNotAllowed this error you got because you have submit form using patch method and you define post method in route. so should be used
Route::patch('admin/pages/admin/data', 'Admin\DTRDataController#update');
instead of
Route::post('admin/pages/admin/data', 'Admin\DTRDataController#update');
Route::patch('admin/pages/admin/data', 'Admin\DTRDataController#update');

Yii2 URL manager rules and forms with GET method

I have a global search form that submits to search action of a controller:
<?=Html::beginForm(['/feqh/search'], 'get', ['class' => 'navbar-form navbar-left', 'role' => 'search', 'id' => 'searchForm']);?>
<div class="form-group has-feedback Right">
<input id="q" type="text" class="form-control" placeholder="<?=yii::t('app','Search');?>" name="q" value="<?= Html::encode(\Yii::$app->getRequest()->getQueryParam('q',""));?>" />
<i class="form-control-feedback glyphicon glyphicon-search"></i>
</div>
<button type="submit" class="btn btn-default"><?=yii::t('app','Submit');?> <i class="glyphicon glyphicon-ok"></i></button>
</form>
I decided to make pretty URL for search through rules as following:
'search/<q:\w+>' => 'feqh/search',
However, submitting the form always generate the following URL:
example.com/feqh/search?q=anySearchString
However, example.com/search/anySearchString is accessible. Here the problem with submitting using the form.
I tried to change the form action URL:
<?=Html::beginForm(['feqh/search'] i.e removing the initial / but It does not make any difference.
By the way, the following rule is working too:
'search' => 'feqh/search', it makes example.com/search?q=anySearchString. However, the applying of this rule preventexample.com/search/anySearchString`
This has nothing to do with your pretty URL configuration (and not even Yii)... It's a browser thing. It only knows how to submit a form is posted as either a GET or a POST.
So since you are posting in GET mode it will simply add the inputs as query parameters to your URL.
If you want the URL in the address bar to represent your pretty URL you'll have to take control over the submit and perhaps issue a redirect instead?
$('#searchForm').submit(function() {
window.location = $(this).attr("action") + '/' + $('#q').val();
return false;
});
It's the only way I can think of right now.
You can try something like:
'search/<q:w>' => 'feqh/search/variable_name/<q>'
Then in your
actionSearch()
Do something like
$query = isset($_REQUEST['variable_name']) ? $_REQUEST['variable_name'] : '';
Or u can try to make redirect action next to your search action and change the search form so it leads to redirect.
Put this into common/main.php rules(advanced app):
'controller/action/<param:[\w-]+>/<page:[\d]+>' => 'controller/action',
'controller/action/<param>' => 'controller/action',
You need to change "controller","action", and "param" into your controller action and parameter.
This is mainly for search problem i encountered so i posted it here in hope it helps someone.

Persist Form Data Across Multiple Steps in Laravel

When I've made multistep forms in the past I would generally store the form data in the session before returning it to the view, that way the data persists if the user refreshes the page or clicks the browser's native back buttons.
Transferring my past logic to Laravel I built the following form consisting of three stages:
[Input -> Confirm -> Success]
Routes.php
Route::group(array('prefix' => 'account'), function(){
Route::get('register', array(
'before' => 'guest',
'as' => 'account-create',
'uses' => 'AccountController#getCreate'
));
Route::post('register', array(
'before' => 'guest|csrf',
'as' => 'account-create-post',
'uses' => 'AccountController#postCreate'
));
Route::get('register/confirm', array(
'before' => 'guest',
'as' => 'account-create-confirm',
'uses' => 'AccountController#getCreateConfirm'
));
Route::post('register/confirm', array(
'before' => 'guest|csrf',
'as' => 'account-create-confirm-post',
'uses' => 'AccountController#postCreateConfirm'
));
Route::get('register/complete', array(
'before' => 'guest',
'as' => 'account-create-complete',
'uses' => 'AccountController#getCreateComplete'
));
});
AccountController.php
<?php
class AccountController extends BaseController {
private $form_session = 'register_form';
public function getCreate()
{
if(Session::has($this->form_session))
{
// get forms session data
$data = Session::get($this->form_session);
// clear forms session data
Session::forget($this->form_session);
// load the form view /w the session data as input
return View::make('account.create')->with('input',$data);
}
return View::make('account.create');
}
public function postCreate()
{
// set the form input to the session
Session::set($this->form_session, Input::all());
$validation_rules = array(
'email' => 'required|max:50|email|unique:users',
'password' => 'required|max:60|min:6',
'password_conf' => 'required|max:60|same:password'
);
$validator = Validator::make(Input::all(), $validation_rules);
// get forms session data
$data = Session::get($this->form_session);
// Return back to form w/ validation errors & session data as input
if($validator->fails()) {
return Redirect::back()->withErrors($validator);
}
// redirect to the confirm step
return Redirect::route('account-create-confirm');
}
public function getCreateConfirm()
{
// prevent access without filling out step1
if(!Session::has($this->form_session)) {
return Redirect::route('account-create');
}
// get forms session data
$data = Session::get($this->form_session);
// retun the confirm view w/ session data as input
return View::make('account.create-confirm')->with('input', $data);
}
public function postCreateConfirm()
{
$data = Session::get($this->form_session);
// insert into DB
// send emails
// etc.
// clear forms session data
Session::forget($this->form_session);
// redirect to the complete/success step
return Redirect::route('account-create-complete');
}
public function getCreateComplete() {
return View::make('account.create-complete');
}
}
create.blade.php
<form action="{{ URL::route('account-create-post') }}" method="post">
Email: <input type="text" name="email" value="{{ (isset($input['email'])) ? e($input['email']) : '' }}">
#if($errors->has('email'))
{{ $errors->first('email') }}
#endif
<br />
Password: <input type="text" name="password" value="">
#if($errors->has('password'))
{{ $errors->first('password') }}
#endif
<br />
Password Confirm: <input type="text" name="password_conf" value="">
#if($errors->has('password_conf'))
{{ $errors->first('password_conf') }}
#endif
<br />
{{ Form::token() }}
<input type="submit" value="Confirm">
</form>
create-confirm.blade.php
Email: {{ $input['email']; }}
Password: {{ $input['password']; }}
<form action="{{ URL::route('account-create-confirm-post') }}" method="post">
{{ Form::token() }}
return
<input type="submit" name="submit_forward" value="Submit">
</form>
The above works fine, however I am wondering if this is the best way to approach multi-step forms in Laravel?
When I have created multi-part forms, I have always done it in a way so that the user can always come back and finish the form later, by making each form persist what it has to the database.
For instance
Step 1 - Account Creation
I would have the user create their authentication details at this step, create the user account (with password) here and also log the user in, redirecting to the dashboard. There I can do a check to see if the user has a profile and if they don't, redirect them to the profile creation form.
Step 2 - Profile Creation
Because we have an authenticated user, the profile creation form can save its data to the currently logged in user. Subsequent sections follow the same process but check the existence of the previous step.
Your question seems to be about confirming whether a user wishes to create an account. What I would do in your situation would be, on the form you created to confirm the user account, I would keep the user's data in hidden input fields.
Email: {{ $input['email'] }}
Password: {{ $input['password'] }}
<form action="{{ URL::route('account-create-confirm-post') }}" method="post">
<input type="hidden" name="email" value="{{ $input['email'] }}">
<input type="hidden" name="password" value="{{ $input['password'] }}">
{{ Form::token() }}
return
<input type="submit" name="submit_forward" value="Submit">
</form>
Although displaying the user's chosen password back to them on this page seems to be a bit superfluous when you ask them to confirm their password on the previous page, plus some users might question why their password is being shown in plaintext on the screen, especially if they are accessing the site from a public computer.
The third option I would suggest would be to create the user account and soft-delete it (Laravel 4.2 Docs / Laravel 5 Docs), returning the user's account number to the new form:
Email: {{ $input['email'] }}
Password: {{ $input['password'] }}
<form action="{{ URL::route('account-create-confirm-post') }}" method="post">
<input type="hidden" name="id" value="{{ $user_id }}">
{{ Form::token() }}
return
<input type="submit" name="submit_forward" value="Submit">
</form>
then undo the soft-delete when the user confirms their account. This has the added bonus that you could track people trying to sign up multiple times for an account and not completing the process and see if there's a problem with your UX.
Conclusion
Of course, you could also still do it the way you always have with a session, all I have tried to do here is show you some other ways you can approach it, as with everything to do with the best way of doing something, this is a highly opinionated subject and is likely to get many opposing views on how it should be done. The best way to do it is the way that works best for you and your users... mainly your users.
There are two ways to do it (that i can think of). I prefer second one.
Client side - everything can be handled by javascript. Basic validation (if field is email, if field has enough characters etc.) would be checked with javascript. After confirmation, AJAX request would go through server side validation and if anything went wrong you could highlight invalid inputs. "check if email is available" button (via AJAX) would be great too.
Server side - pretty much what you did but I would move it to service - it would make it much cleaner.
public function getCreate() {
if ($this->formRememberService->hasData()) {
return View::make('account.create')
->with('input', $this->formRememberService->getData());
}
return View::make('account.create');
}
public function postCreate() {
$this->formRememberService->saveData(Input::all());
// ...
}
public function postCreateConfirm() {
// ...
$this->formRememberService->clear();
return Redirect::route('account-create-complete');
}
Adding "forget me" action would be nice (especially if form requires more private data).
Why getCreate() has Session::forget()? If someone goes back to change something and accidently leaves your site his data will be lost.
1st) Create a custom hidden field in the form containing a random md5 character set to submit it with the form... (it can be the timestamp, the user ip address, and country concatenated together as 3 md5 strings separated by whatever character , or #, so it can be working as a token of the form)
2nd) pass the hidden field into your controller and validate it after getting the user input from the form by generating the same values in your controller, encrypting these values as md5 too, then concatenate them all together, and compare the values that is coming from the user input form with the values you are generating in your controller.
3rd) Put the values of the form in your controller in a session then regenerate the session id every visit to every view the user is going to visit.
4th) update the timestamp in your session according the timestamp the user is visiting every page.
Just because you know Laravel, does not mean you have to do everything in Laravel.
Multi-step forms should never involve server-side magic. The best and easiest you can do is to hide certain steps with display:none; and switch to the next step using javascript toggling visibilities only.

Categories