I'm busy migrating an app from Laravel 4.2 to 5.1, and am experiencing an issue with Form Requests. It's probably something simple that I'm missing - not really sure.
The method below is meant to attempt a sign in if SignInRequest successfully authorises the request. However, if the validation passes, SignInRequest is not passed to attemptSignIn, which throws the process off with this error:
Argument 2 passed to App\Http\Controllers\Auth\AuthController::attemptSignIn() must be an instance of App\Http\Requests\SignInRequest, none given
This is the method (controller is AuthController) in question, which tries to sign in using a username or an email address:
public function attemptSignIn($type = 'regular', SignInRequest $request)
{
switch ($type) {
case 'regular':
$identifierFieldName = 'account_identifier';
$field = filter_var($request->input($identifierFieldName), FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
$request->merge([$field => $request->input($identifierFieldName)]);
$specifics = $request->only($field, 'passphrase');
$specifics['activated'] = (int) true;
if ($this->auth->attempt($specifics)) {
return redirect()->intended($this->redirectPath);
} else {
return redirect($this->signInPath)
->with('authError', "The credentials you've provided are incorrect.")
->with('authErrorType', 'warning')
->withInput($request->only($identifierFieldName))
->withErrors();
}
break;
case 'oota':
break;
default:
return redirect($this->signInPath);
break;
}
}
The form request is straight-forward, specifying rules, messages and authorize (return true).
If the validation fails, however, SignInRequest is passed to the method, and the errors are shown accordingly.
Am I missing anything here?
use \path\to\your\ClassRequest as MyRequest
or
protected $myrequest;
public function __construct(\path\to\your\ClassRequest $request){
$this -> myrequest = $request; // use directly in your method or any method
}
Hope this help.
public function post_form(MyRequest $request) {
your code here
}
Related
public function addContact()
{
if (!$this->validate()) {
return null;
}
$model = new ContactForm();
foreach (['name', 'email', 'subject', 'body'] as $property) {
$model->$property = $this->$property;
}
return $model->save();
}
I get this error:
Calling unknown method: frontend\models\ContactForm::save()
the var_dump is working properly.
the save method return this error [save method .
This is the complete model rules :
rules model
and this is the rest of the model code :
rest of model
Could someone tell me what I'm getting wrong?
As Sfili_81 already mentioned you have to extend ActiveRecord instead of Model to be able call the save method. But that only makes sense if you want to save the data to a database.
You can use the default logic of the yii2-app-basic to send the data submitted via ContactForm to a configured admin via email. To do so you just have to call the contact method of ContactForm.
// SiteController::actionContact
$model = new ContactForm();
if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
Yii::$app->session->setFlash('contactFormSubmitted');
return $this->refresh();
}
If you want to receive real emails you also have to set the useFileTransport property of the mailer component to false (in config/web.php), otherwise every mail gets saved to runtime/mail.
I'm trying to find the correct way to lay this out in laravel so that I can hit one GET route which calls a single function, but within that function I want to use data from a mysql table to determine which blade to show.
Say this URL is visited with a query string parameter:
www.testsite.com?email=testEmail.com
I hit this route (But not sure how to accept the parameter)
Route::get('register', 'Data\DataController#DataForm')
->name('Data.register');
I have a mysql table called dataTable set up like so
email | type
-------------------------
test1#mail.com A
test2#mail.com B
test3#mail.com C
What's the best way to incorporate the email parameter so that I can hit the single route and single function, then use the email/type columns from mysql to determine the appropriate blade to show?
public function DataForm(Request $request)
{
//query table based on query string parameter 'email'
$email = dataTable::where('email', /*email parameter?*/)->first();
if($email['type']== A){
return view('data.typeA');
}elseif($email['type']== B){
return view('data.typeB');
}elseif($email['type']== C){
return view('data.typeC');
}
}
You can add it as a route parameter :
Route::get('register/{email}', 'Data\DataController#DataForm')->name('Data.register');
And then inside controller :
public function DataForm($email)
{
// Abort to 404 if $email is not a valid email address
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
abort(404);
}
$typeData = Model::where('email', $email)->first();
// Email not found in the database
if(!$typeData){
abort(404);
}
switch ($typeData->type) {
case 'A':
return view('data.typeA');
case 'B':
return view('data.typeB');
case 'C':
return view('data.typeAC');
default:
abort(404);
}
}
Using a slash in the route you should be able to pass it like this:
Route::get('register/{email}', 'Data\DataController#DataForm')
->name('Data.register');
Then in your function you can just use the variable $email and it contains the email in the URL. But not sure if this URL is OK, using a slash instead of '?email=' for the passing of the parameter.
More on there here:
https://laravel.com/docs/5.7/routing#required-parameters
You'd need more logic than this, obviously:
public function DataForm(Request $request)
{
$type = (dataTable::where('email', $request->query->get('email'))->first())['type'];
return view('data.type'.$type);
}
As others said you could auto type in Request $request; however you don't have to. You can keep the function definition without the passed parameter, and use the helper functions instead:
if(request()->has('email')) {
$email = request()->input('email');
// Do stuff
} else {
// No email supplied
}
I tried extending the Laravel 4.2 validation to check if all form data is valid (not just some fields) according to some business rules.
I could not extend the custom validator in any normal way (either too hacky or not DRY enough) so I decided to add the business rules check after validator succeeds, and then add my own error messages and redirect.
It is still ugly and it does not work but for now I would be happy if someone tells me how to add my message to the session. If you feel like telling me how I should use Laravel's custom validators I've included some more code so you can be more specific..:
$validator = Validator::make(Input::all(), $this->validationRules, $this->validationMessages);
if ($validator->fails()) {
if (0 == $id) {
return Redirect::route('x.create')->withErrors($validator)->withInput();
} else {
return Redirect::route('x.edit', $id)->withErrors($validator)->withInput();
}
} else {
//TESTs
if ($this->AlreadyExists($id)) {
$messages = new MessageBag();
if ($request->getSession()->get('errors')){
$messages = $request->getSession()->get('errors')->getBag('default');
}
$messages->add('form', 'same data already exists');
if (0 == $id) {
return Redirect::route('x.create')->withErrors($messages)->withInput();
} else {
return Redirect::route('x.edit', $id)->withErrors($messages)->withInput();
}
}
}
//all is ok. Save/update entity
...
The code for setting the session is first 5 lines after check for AlreadyExists. I got it from some forum but it doesn't seem to work ok (I get some "non-object" exceptions if I include the code so it seems it corrupts the session object)
I don't think I have time to upgrade to L5.
I solved this by creating a new error message bag and adding it to redirect:
$mb = new Illuminate\Support\MessageBag();
$mb->add("form", "same data already exists");
...
return Redirect::route('x.create')->withErrors($mb)->withInput();
Seems I needed to add full namespace for bag, but most of all I needed rest :)
The validation logic is still bad but no time to tinker with that now.
for anyone who uses form request I suggest to take a look at this implementations. formatErrors in laravel 5.1 documentations mentioned for a way to manipulate the way errors display. I just simply jump in it and added some codes to extend errors.
use Illuminate\Contracts\Validation\Validator;
class ProfileChangePasswordRequest extends Request
{
public function authorize()
{
if (auth()->check())
return true;
return false;
}
public function rules()
{
return [
'password' => "required|confirmed|min:6"
];
}
protected function formatErrors(Validator $validator)
{
if ($this->isCurrentPasswordValidate()) {
return $validator->errors()->all();
} else {
$validator->errors()->add('current_password', 'current password is wrong');
return parent::formatErrors($validator);
}
}
public function isCurrentPasswordValidate()
{
return auth()->validate(['email' => auth()->user()->email,
'password' => $this->current_password]);
}
}
In my controller/action:
if(!empty($_POST))
{
if(Auth::attempt(Input::get('data')))
{
return Redirect::intended();
}
else
{
Session::flash('error_message','');
}
}
Is there a method in Laravel to check if the request is POST or GET?
According to Laravels docs, there's a Request method to check it, so you could just do:
$method = Request::method();
or
if (Request::isMethod('post'))
{
//
}
The solutions above are outdated.
As per Laravel documentation:
$method = $request->method();
if ($request->isMethod('post')) {
//
}
I've solve my problem like below in laravel version: 7+
In routes/web.php:
Route::post('url', YourController#yourMethod);
In app/Http/Controllers:
public function yourMethod(Request $request) {
switch ($request->method()) {
case 'POST':
// do anything in 'post request';
break;
case 'GET':
// do anything in 'get request';
break;
default:
// invalid request
break;
}
}
Of course there is a method to find out the type of the request, But instead you should define a route that handles POST requests, thus you don't need a conditional statement.
routes.php
Route::post('url', YourController#yourPostMethod);
inside you controller/action
if(Auth::attempt(Input::get('data')))
{
return Redirect::intended();
}
//You don't need else since you return.
Session::flash('error_message','');
The same goes for GET request.
Route::get('url', YourController#yourGetMethod);
Use Request::getMethod() to get method used for current request, but this should be rarely be needed as Laravel would call right method of your controller, depending on request type (i.e. getFoo() for GET and postFoo() for POST).
$_SERVER['REQUEST_METHOD'] is used for that.
It returns one of the following:
'GET'
'HEAD'
'POST'
'PUT'
After a user fails authorisation I'd like to forward them back to the login page. Currently the _forward method causes Zend to hang and give a time out error (30 seconds exceeded).
The code for the login page handles both a login and signup form, and forwards to the authorisation controller:
public function indexAction() {
if ($this->_request->isPost()) {
$formData = $this->_request->getPost();
if (array_key_exists('signUp', $formData)) {
$authAction = 'signup';
$form = 'signupForm';
} elseif (array_key_exists('logIn', $formData)) {
$authAction = 'login';
$form = 'loginForm';
}
if ($this->$form->isValid($formData)) {
$this->_forward($authAction, 'user-auth', null, $formData);
} else {
$this->$form->populate($formData);
}
}
}
This works fine and redirects to the auth controller successfully. The code inside the login action of the auth controller is as such:
public function loginAction() {
$formData = $this->_request->getPost();
$authAdapter = new My_Auth_Adapter();
$authAdapter->setTableName('user')
->setIdentity($formData['username'])
->setCredential($formData['password'])
->setIdentityColumn('username')
->setCredentialColumn('password');
$result = $authAdapter->authenticate();
if ($result->isValid()) {
// success, all good
} else {
$this->_forward('index', 'login', 'default', $formData);
}
}
We arrive here fine, and a successful authorisation works as expected. However in my else statement placing another forward back to the original login controller (I wish to populate the username as well as post back an error message) causes the program to hang, although a redirect works fine.
I thought it may be because the login controller is re-detecting the post data and I'm getting caught in an infinite loop, but removing the $formData as the last argument of the forward doesn't change anything.
I've also tried $formData['errMsg'] = 'whatever' above the forward and then checking if the key exists or if it is set in the login controller, but that doesn't change a thing either.
Interestingly, the time out error I receive references the Auth DbTable Adapter:
Fatal error: Maximum execution time of 30 seconds exceeded in /Applications/MAMP/MampServer/mysite/library/Zend/Auth/Adapter/DbTable.php on line 174
Any ideas as to what may be happening?
I think you are infinity looping between loginAction() and indexAction().
Check out the difference between the calls to forward() and redirect() action helpers. The former, forward() internally will change the $request->isDispatched() == false - This means that the front controller will execute the targeted controller action without a new HTTP request.
The outcome of this is that $this->_request->isPost() will always be true and therefore $this->$form->isValid($formData) again will also be true, meaning your going around in circles.
I know the below is a very different to your approach, however I believe it is a more conventional separation of concerns for Zend 1 controllers.
// ... SomeController.php
public function getLoginForm();
public function getSignupForm();
protected function authenticate($username, $password)
{
$authAdapter = new My_Auth_Adapter();
$authAdapter->setTableName('user')
->setIdentity($username)
->setCredential($password)
->setIdentityColumn('username')
->setCredentialColumn('password');
$result = $authAdapter->authenticate();
return ($result->isValid()) ? true : false;
}
public function indexAction()
{
$form = $this->getLoginForm();
$request = $this->getRequest();
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
if ($this->authenticate($form->getValue('username'), $form->getValue('username'))) {
$this->redirect('/members'); // Successfully logged in
}
}
}
$this->view->form = $form;
}
public function signupAction()
{
// stuff only for signups!
}
Edit To elaborate: forward() is a controller action helper. Its job is simply to modify the Zend_Controller_Request_Http instance. The Zend_Controller_Request_Http class is the one returned when you call $this->getRequest() within a controller.
The Request instance encapsulates all access to $_POST, $_GET and stores then as values within the object. Calls such as $request->setParam('someparam', 123) set or get these values rather than the standard direct access to $_POST['someparam'] or $_GET['someparam'].
The special case is with the values module,controller,action and dispatched. These are the key's used by the Zend_Controller_Front and the Dispatcher when trying to determine the correct controller to instantiate and action method to execute.
A simplified example of how the dispatch loop works:
while(! $request->isDispatched()) {
$request->setDispatched(true);
// If at any point here we change setDispatched(true)
// perhaps in a controller action with a call to forward()
// then the whole dispatch loop will be called again
// perhaps creating a different controller
$controllerName = $request->getControllerName();
$actionName = $request->getActionName();
$controller = new $controllerName();
$controller->$actionName();
}
In the else block:
$this->_redirect($this->url(array('login' => $formData['username'], 'nameOfYourRoute'));
Added a new get variable 'login' to your route and populate your forms login with this variable.