ZF2 FormInput to show error class on validation fail - zend-framework2

My form has multiple elements being rendered in the format:
<div class="form-group">
<?php echo $this->formlabel($form->get('lastname')); ?>
<?php echo $this->forminput($form->get('lastname')); ?>
<?php echo $this->formElementErrors($form->get('lastname')); ?>
</div>
I do this so I can have my element next to my label as opposed to inside it:
<label for="lastname">Lastname</label><input .../>
<ul><li>error messages</li></ul>
What I have noticed is that on validation fail the input is not getting the input-error class. When I change the above code to <?php echo $this->formrow($form->get('lastname')); ?> the input is put into the label (which I don't want) and the input gets the error class as expected:
<label>Lastname<input ... class="input-error"/></label>
How do I get the input-error class into the element via $this->forminput?
When I do formrow before forminput then the input in both has the error class, but when I do forminput on it's own it does not.
[edit]
Short term I have put formrow (without the echo) above my existing code and now my input field is showing the error class, but that feels like a bit of a hack, and I would have to do that for each element in my app that I have set up like this.

I have created a view helper to add the missing class to forminput:
<?php
/**
* Extend zend form view helper forminput to add error class to element on validation
* fail
*
* #package RPK
* #author Richard Parnaby-King
*/
namespace RPK\Form\View\Helper;
use Zend\Form\View\Helper\FormInput as ZendFormInput;
class FormInput extends ZendFormInput
{
protected $inputErrorClass = 'input-error';
/**
* Render a form <input> element from the provided $element
*
* #param ElementInterface $element
* #throws Exception\DomainException
* #return string
*/
public function render(\Zend\Form\ElementInterface $element)
{
$inputErrorClass = $this->inputErrorClass;
// Following code block copied from \Zend\Form\View\Helper\FormRow
// Does this element have errors ?
if (count($element->getMessages()) > 0 && !empty($inputErrorClass)) {
$classAttributes = ($element->hasAttribute('class') ? $element->getAttribute('class') . ' ' : '');
$classAttributes = $classAttributes . $inputErrorClass;
$element->setAttribute('class', $classAttributes);
}
return parent::render($element);
}
}
I then tell my application to use this view helper in my Module.php file:
public function onBootstrap(MvcEvent $e) {
$services = $e->getApplication()->getServiceManager();
//add custom forminput viewhelper
$services->get('ViewHelperManager')->setFactory('forminput', function (\Zend\View\HelperPluginManager $manager) {
return new \RPK\Form\View\Helper\FormInput();
});
}

Related

Problem to get logged in with Laravel and docker on production

I have a Laravel application and I am currently working on integrating docker. The app runs perfectly locally but on production I simply can't log in. Every time I submit the log in form I get redirected to the log in form without any message of success nor failure.
I have realized that the request it reaches the controller it should but it does not reach the action. I put a die command in the constructor and it worked but it didn't when I did the same in the first line of the controller's action.
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Monolog\Logger;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class AuthController extends Controller
{
/*
|---------------------------------------------------------------------
| Registration & Login Controller
|---------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login / registration.
*
* #var string
*/
protected $redirectTo = '/';
/**
* Create a new authentication controller instance.
*
* #return void
*/
public function __construct()
{
// die('something') works here
$this->middleware('guest', ['except' => ['logout', 'register', 'showRegistrationForm']]);
// die('something') works here too
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'rut' => 'required|max:30',
'apellidos' => 'required|max:255',
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(array $data)
{
}
public function login(Request $request)
{
//die('something'); doesn't work here
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
}
I don't know if you still need it, but I had the same problem. You need to modify the paths in bootstrap/cache/config.php with the path that you set it for WORKDIR in Dockerfile.

Fluent Validation, MVC: Triggering Client-Side RuleSet Validation on Button Click

Context
In a view, I've created one form for my view model. I've separated the form over multiple sections using the Twitter Bootstrap Wizard plugin. The user can access the next section by clicking a "Next" button. Each section has a Fluent Validation RuleSet defined for the model properties on that section. The rules I've defined in each RuleSet are compatible with Fluent Validaiton's client-side validation.
Question
Upon clicking the next button, what's the best way:
To get the validation state for only the current section's RuleSet on the client-side with Fluent Validation?
To get Fluent Validation to display client-side validation for only the current section's RuleSet?
What I've Tried
I've read the Fluent Validation start guide and this question. While they demonstrate how to achieve what I'm looking for on the server-side, they don't seem to address my client-side questions. I bold "seem" because I'm reasonably new to Fluent Validation and MVC, so, I may have misunderstood the links' content.
You can use commands from the jQuery validation library, which Fluent Validation uses.
In an element in your form, define an attribute that will help you recognise a validation group. For example
#Html.EditorFor(model => model.GroupA_TextA, new { htmlAttributes = new { #class = "form-control", data_validation_group = "GroupA" }})
Use the .settings.ignore syntax from the jQuery validation library to control which groups to validate.
I've made a class that exposes functionality to validate a group in, and the entirety of, a Fluent Validation validated form. I've included the TypeScript and transpiled JavaScript below.
TypeScript
/**
* For validating a form when Fluent Validation is used for model valdiation.
*/
interface IFluentValidationFormValidator {
/**
* The form to validate.
*/
form: JQuery<HTMLElement>;
/**
* The name of the validation group to validate.
*/
group: string;
/**
* Validate the entire form.
*/
validate(): boolean;
/**
* Validate a validation group in the form.
* #param group The name of the validation group to validate.
*/
validateGroup(): boolean;
}
/**
*
*/
class StandardFluentValidationFormValidator implements IFluentValidationFormValidator {
/**
* #inheritdoc
*/
form: JQuery<HTMLElement>;
/**
* #inheritdoc
*/
group: string;
/**
* #inheritdoc
*/
validate(): boolean {
const formValidator = this.form.validate();
formValidator.form();
return formValidator.valid();
}
/**
* #inheritdoc
*/
validateGroup(): boolean {
// The form validator.
const formValidator = this.form.validate();
// Perform standard validation on form if the validation group is undefined.
if (this.group === undefined) {
formValidator.form();
return formValidator.valid();
}
// Current group validation settings.
const initialValidateIgnoreSetting = formValidator.settings.ignore;
// Ignore all elements but the group.
formValidator.settings.ignore += `,:not([data-validation-group=${this.group}])`;
// Valdiate the form.
formValidator.form();
// Reset group validation settings.
formValidator.settings.ignore = initialValidateIgnoreSetting;
// Return the validation state.
return formValidator.valid();
}
}
JavaScript
"use strict";
var StandardFluentValidationFormValidator = (function () {
function StandardFluentValidationFormValidator() {
}
StandardFluentValidationFormValidator.prototype.validate = function () {
var formValidator = this.form.validate();
formValidator.form();
return formValidator.valid();
};
StandardFluentValidationFormValidator.prototype.validateGroup = function () {
var formValidator = this.form.validate();
if (this.group === undefined) {
formValidator.form();
return formValidator.valid();
}
var initialValidateIgnoreSetting = formValidator.settings.ignore;
formValidator.settings.ignore += ",:not([data-validation-group=" + this.group + "])";
formValidator.form();
formValidator.settings.ignore = initialValidateIgnoreSetting;
return formValidator.valid();
};
return StandardFluentValidationFormValidator;
}());

Render partial in other module ZF2

In my project I have to modules
Module1
Module2
In Module1 I have a view that needs to render a view that I have in Module2, so what I'm doing is:
$this->partial('partials/hello/title.phtml','Module2',array('data' => $data))
and seems I'm calling correctly the view, but inside of the view title.phtml I'm unable to use data
Undefined variable: data in /site/src/module/Module2/view/partials/hello/title.phtml
Do I need add something related to the configuration?
Thanks!
You aren't calling it correctly, try:
$this->partial('partials/hello/title.phtml', array('data' => $data));
See: http://framework.zend.com/manual/2.3/en/modules/zend.view.helpers.partial.html
The fact that the partial is in a different module doesn't matter. Having to specify the module name as the second parameter was a thing in ZF1 only.
Another solution is add your own view-path resolver in module-bootstrap:
class Module
{
/* #var \Zend\ServiceManager\ServiceManager $SM */
public static $SM;
/* #var \Zend\EventManager\EventManager $EM */
public static $EM;
public function onBootstrap(MvcEvent $e)
{
self::$SM = $e->getApplication()->getServiceManager();
self::$EM = $e->getApplication()->getEventManager();
//change view resolver to resolve views from {Module}/view/{Controller}/{Action}.phtml path
self::$EM->attach('dispatch', function($e) {
self::$SM->get('ViewRenderer')->resolver()->attach(
new \Engine\View\Resolver\MCA() , 10
);
});
}
....
Resolver gets a string, and must to return path or false (if not resolved), just write resolver to understand other-modules paths:
<?php
namespace Engine\View\Resolver;
use Zend\View\Renderer\RendererInterface as Renderer;
/**
* Resolves view scripts based on a stack of paths
*/
class MCA implements \Zend\View\Resolver\ResolverInterface
{
public function resolve($name, Renderer $renderer = null)
{
$path = explode('/', $name);
if (count($path)<3){
return false;
}
$module = array_shift($path);
$resolvedPath = ROOT_PATH . '/module/'. ucfirst($module) . '/view/' . implode('/', $path). '.phtml';
if (!file_exists($resolvedPath)){
return false;
}
return $resolvedPath;
}
}
There can be collisions, but you can regulate priority of resolvers (10 in example).

How do I send the same header with every response in zf2?

I would like to send the header "X-Content-Type-Options: nosniff" back with every response in my zend framework 2 application. How can I do that without explicitly coding it in every single controller method?
You can modify the response object via the bootstrap:
Module.php
/**
* On bootstrap event
*
* #param \Zend\Mvc\MvcEvent $e
*/
public function onBootstrap(MvcEvent $e)
{
$headers = $e->getResponse()->getHeaders();
$headers->addHeaderLine('X-Content-Type-Options: nosniff');
}

return json to ajax in symfony?

In symfony, I call an action and I want this to return json to jQuery frontend.
The Jobeet tutorial teaches how to return a partial but I want to return json, not a partial.
If it's just a normal AJAX action you're returning it from, I think I've used the following somewhere in the past:
return $this->renderText(json_encode($something));
The cheap way:
function executeSomethingThatReturnsJson(){
$M = new Model();
$stuff = $M->getStuff();
echo json_encode($stuff);
die(); //don't do any view stuff
}
The smarter way:
A smarter way is to create a nice subclass of sfActions that helps handling json-stuff.
In a project I did recently, I created a application called 'api' (./symfony generate:application api)
and then created a file like:
api/lib/apiActions.class.php
<?PHP
class apiActions extends sfActions {
public function returnJson($data){
$this->data = $data;
if (sfConfig::get('sf_environment') == 'dev' && !$this->getRequest()->isXmlHttpRequest()){
$this->setLayout('json_debug');
$this->setTemplate('json_debug','main');
}else{
$this->getResponse()->setHttpHeader('Content-type','application/json');
$this->setLayout('json');
$this->setTemplate('json','main');
}
}
}
Notice that I explicitly set the template there.
So my jsonSuccess.php template is simply:
<?PHP echo json_encode($data);
While json_debugSuccess.php makes things prettier:
<?PHP var_dump($data); ?>
Then you can have a controller that extends apiActions (instead of the usual sfActions) that looks like this:
<?php
class myActions extends apiAction {
public function executeList(sfWebRequest $request)
{
$params = array();
if ($request->hasParameter('id')){
$id = $request->getParameter('id');
if (is_numeric($id)){
$params['id'] = $id;
}
}
$data = Doctrine::getTable('SomeTable')->findAll();
$this->returnJson($data);
}
}
Disclaimer: The code above is copy/pasted out of an app I have, but simplified. It's for illustrative purposes only -- but it should get you heading in the right direction.
FYI: In case of Symfony 2.x "quick and dirty" way looks like this:
return new Response(json_encode($data), 200, array('Content-Type', 'text/json'));
Return new JsonResponse(array);

Resources